#!/usr/bin/env python3
#vim: set filetype=python

import logging
import json
import enum
import pathlib
import sys
import argparse
#import optparse
import subprocess
import os


from typing import (Optional, Any, TypeAlias, Literal, cast,)

logger = logging.getLogger()

def js(argv):
    return subprocess.check_call([
        'sudo',
        'docker-compose',
        '--project-directory',
        os.path.abspath(
            os.path.dirname(__file__),
        ),
        '-f',
        os.path.abspath(
            os.path.join(
                os.path.dirname(__file__),
                'docker', 'js',
                'docker-compose.yml',
            )
        ),
        *argv,
    ])

def env(
    argv: Optional[list[str]] = None,
    **kwargs: Any,
) -> Optional[subprocess.CompletedProcess]:
    env_path = pathlib.Path(__file__).parent / 'tmp' / 'env3'

    if not env_path.exists():
        subprocess.check_call([
            sys.executable, '-m', 'venv',
            '--system-site-packages',
            str(env_path)
        ])

        subprocess.check_call([
            env_path / 'bin' / 'python3',
            '-m', 'pip',
            'install', '-r', 'requirements.txt',
        ])

    if not argv is None:
        return subprocess.run([
            str(env_path / 'bin' / 'python3'),
            *argv,
        ], **kwargs)

    return None

def ruff(args: list[str]) -> None:
    res = env([
        '-m',
        'ruff',
        'check',
        *args,
        '--output-format', 'json',
        '--ignore', ','.join([
            'E731',
            'E713',
            'E714',
            'E703',
        ]),
        '.',
        'dotfiles/.local/bin/commands',
    ], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    assert not res is None

    errors = json.loads(res.stdout.decode('utf-8'))

    g: dict[str, Any] = dict()
    for o in errors:
        if not o['filename'] in g:
            g[o['filename']] = []
        g[o['filename']].append(o)

    h = {
        k : len(v)
        for k, v in g.items()
    }

    logger.info(json.dumps(errors, indent=4))
    logger.info(json.dumps(h, indent=4))

def mypy(args: list[str]) -> None:
    res = env([
        '-m',
        'mypy',
        '--strict',
        '-O',
        'json',
        *args,
        'dotfiles/.local/bin/commands',
        'python',
        'm.py',
    ], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    assert not res is None

    errors = [
        json.loads(o)
        for o in res.stdout.decode('utf-8').splitlines()
    ]

    g : dict[str, Any] = dict()
    for o in errors:
        if not o['file'] in g:
            g[o['file']] = []
        g[o['file']].append(o)

    h = {
        k : len(v)
        for k, v in g.items()
    }

    logger.info(json.dumps(errors, indent=4))
    logger.info(json.dumps(h, indent=4))

def inside_env() -> bool:
    try:
        import numpy
        return True
    except Exception:
        return False

#class Commands(enum.StrEnum):
#    js = 'js'
#    mypy = 'mypy'
#    env = 'env'
#    ruff = 'ruff'
#    m2 = 'm2'

Command_args = ['js', 'mypy', 'env', 'ruff', 'm2']

Command : TypeAlias = Literal['js', 'mypy', 'env', 'ruff', 'm2']

def run(argv: Optional[list[str]] = None) -> None:
    logging.basicConfig(
        level=logging.INFO,
        format=(
            '%(levelname)s:%(name)s:%(message)s'
            ':%(process)d'
            ':%(asctime)s'
            ':%(pathname)s:%(funcName)s:%(lineno)s'
        ),
    )

    if argv is None:
        argv = sys.argv[1:]


    parser = argparse.ArgumentParser()
    parser.add_argument(
        'command',
        #'_command',
        choices=[
            o
            for o in Command_args
        ],
        #required=True,
    )

    options, args = parser.parse_known_args(argv)

    assert options.command in cast(list[str], Command_args)

    #options.command = Commands(options._command)

    if options.command == 'js':
        js(args)
    elif options.command == 'env':
        env(args)
    elif options.command == 'mypy':
        mypy(args)
    elif options.command == 'ruff':
        ruff(args)
    elif options.command == 'm2':
        if not inside_env():
            env(['--', '_m.py', 'm2', *args])
            return

        import python.tasks.cython
        python.tasks.cython.mypyc_build(
            pathlib.Path('_m.py')
        )
    else:
        raise NotImplementedError

if __name__ == '__main__':
    run(sys.argv[1:])