# -*- coding: utf-8 -*-
# type: ignore

from __future__ import annotations

import os
import typing

from invoke import Context, task
from invoke.exceptions import UnexpectedExit
import jinja2
import werkzeug
import werkzeug.routing

from hotpocket_workspace_tools import get_workspace_mode
from hotpocket_workspace_tools.tasks import (  # noqa: F401
    bump_version,
    get_version,
)

WORKSPACE_MODE = get_workspace_mode()


class PreviewApp:
    ROUTES = werkzeug.routing.Map([
        werkzeug.routing.Rule('/preview/popup.html', endpoint='preview_popup'),
    ])

    def __init__(self):
        here = os.path.dirname(__file__)
        self.jinja_env = jinja2.Environment(
            loader=jinja2.FileSystemLoader(
                [
                    os.path.join(here, 'src/content/templates'),
                ],
            ),
            auto_reload=True,
            autoescape=True,
        )

    @classmethod
    def create_app(cls) -> typing.Self:
        return cls()

    def render_template(self,
                        template_name: str,
                        **context,
                        ) -> werkzeug.Response:
        template = self.jinja_env.get_template(template_name)
        return werkzeug.Response(
            template.render(context), mimetype='text/html',
        )

    def dispatch_request(self,
                         request: werkzeug.Request,
                         ) -> werkzeug.Response | Exception:
        adapter = self.ROUTES.bind_to_environ(request.environ)
        try:
            endpoint, values = adapter.match()
            return getattr(self, f'on_{endpoint}')(request, **values)
        except werkzeug.exceptions.HTTPException as exception:
            return exception

    def on_preview_popup(self, request: werkzeug.Request) -> werkzeug.Response:
        return self.render_template('popup_preview.html')

    def wsgi_app(self,
                 environ: werkzeug.wsgi.WSGIEnvironment,
                 start_response: typing.Any,
                 ) -> werkzeug.Response:
        request = werkzeug.Request(environ)
        response = self.dispatch_request(request)
        return response(environ, start_response)

    def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)


@task
def clean(ctx: Context):
    ctx.run('rm -rf dist/')


@task
def test(ctx: Context):
    # ctx.run('pytest -v --disable-warnings')
    print('NOOP')


@task
def flake8(ctx: Context):
    ctx.run('flake8')


@task
def isort(ctx: Context, check=False, diff=False):
    command_parts = [
        'isort',
    ]

    if check is True:
        command_parts.append('--check')

    if diff is True:
        command_parts.append('--diff')

    command_parts.append('.')

    ctx.run(' '.join(command_parts))


@task
def eslint(ctx: Context):
    ctx.run('yarn run eslint')


@task
def lint(ctx: Context):
    ihazsuccess = True

    try:
        flake8(ctx)
    except UnexpectedExit:
        ihazsuccess = False

    try:
        isort(ctx, check=True)
    except UnexpectedExit:
        ihazsuccess = False

    try:
        eslint(ctx)
    except UnexpectedExit:
        ihazsuccess = False

    if ihazsuccess is False:
        raise RuntimeError('FIAL')


@task
def typecheck(ctx: Context):
    ctx.run('mypy .')


@task
def django_shell(ctx: Context):
    raise NotImplementedError()


@task
def ci(ctx: Context):
    ihazsuccess = True

    ci_tasks = [test, lint, typecheck]
    for ci_task in ci_tasks:
        try:
            ci_task(ctx)
        except UnexpectedExit:
            ihazsuccess = False

    if ihazsuccess is False:
        raise RuntimeError('FIAL')


@task
def setup(ctx: Context):
    print('NOOP')


@task
def start_web(ctx: Context):
    from werkzeug.serving import run_simple
    app = PreviewApp.create_app()

    bind = os.getenv('HOTPOCKET_EXTENSION_PREVIEW_BIND', '127.0.0.1:6524')
    host, port = bind.split(':', maxsplit=1)
    run_simple(host, int(port), app, use_debugger=True, use_reloader=True)


@task
def start_safari(ctx: Context):
    ctx.run('yarn watch:safari')


@task(pre=[clean])
def start_chrome(ctx: Context):
    ctx.run('yarn watch:chrome')


@task(pre=[clean])
def start_firefox(ctx: Context):
    ctx.run('yarn watch:firefox')


@task
def build_safari(ctx: Context):
    ctx.run('yarn build:safari')


@task(pre=[clean])
def build_chrome(ctx: Context):
    ctx.run('yarn build:chrome')

    with ctx.cd('dist/chrome-production'):
        current_version = get_version(ctx)

        ctx.run(' '.join([
            'zip',
            '-r',
            f'../chrome-production-{current_version}.zip',
            '.',
        ]))


@task(pre=[clean])
def build_firefox(ctx: Context):
    ctx.run('yarn build:firefox')

    with ctx.cd('dist/firefox-production'):
        ctx.run(' '.join([
            'web-ext',
            'lint',
        ]))

        current_version = get_version(ctx)

        ctx.run(' '.join([
            'zip',
            '-r',
            f'../firefox-production-{current_version}.zip',
            '.',
        ]))


@task
def build_firefox_source(ctx: Context):
    # AMO requires source bundle to be uploaded alongside the built version.
    ctx.run('rm -rf dist/firefox-source')
    ctx.run('mkdir -p dist/firefox-source dist/firefox-source/assets dist/firefox-source/src')

    ctx.run('rsync -arv assets/ dist/firefox-source/assets/')
    ctx.run('rsync -arv src/ dist/firefox-source/src/')
    ctx.run('rsync -arv eslint.config.js package.json README.md rollup.config.js yarn.lock dist/firefox-source/')

    with ctx.cd('dist/firefox-source'):
        current_version = get_version(ctx)

        ctx.run(' '.join([
            'zip',
            '-r',
            f'../firefox-source-{current_version}.zip',
            '.',
        ]))


@task
def build_opera_source(ctx: Context):
    # Opera requires source bundle to be uploaded alongside the built version.
    ctx.run('rm -rf dist/opera-source')
    ctx.run('mkdir -p dist/opera-source dist/opera-source/assets dist/opera-source/src')

    ctx.run('rsync -arv assets/ dist/opera-source/assets/')
    ctx.run('rsync -arv src/ dist/opera-source/src/')
    ctx.run('rsync -arv eslint.config.js package.json README.md rollup.config.js yarn.lock dist/opera-source/')

    with ctx.cd('dist/opera-source'):
        current_version = get_version(ctx)

        ctx.run(' '.join([
            'zip',
            '-r',
            f'../opera-source-{current_version}.zip',
            '.',
        ]))
