#!/usr/bin/env python3
from pathlib import Path
from pprint import pprint
from itertools import chain
from subprocess import check_call, run, PIPE
import sys
import os
from typing import List, Optional, Iterable


def log(*args):
    print(*args, file=sys.stderr)

CI = 'CI' in os.environ

DIR = Path(__file__).absolute().parent

# hmm. I guess I need to check all subpackages separately
# otherwise pylint doesn't work and mypy doesn't discover everything

# TODO could reuse in readme??
# returns None if not a package
def package_name(p: Path) -> str:
    def mname(p: Path):
        nosuf = p.with_suffix('')
        return str(nosuf).replace('/', '.')

    has_init =  (p.parent / '__init__.py').exists()
    if has_init:
        return mname(p.parent)
    else:
        return mname(p)

def subpackages(package: str) -> Iterable[str]:
    ppath = package.replace('.', '/')
    yield from sorted({
        package_name(p.relative_to(DIR)) for p in (DIR / ppath).rglob('*.py')
    })


# TODO meh.. think how to check _everything_ on CI
def core_modules() -> Iterable[str]:
    return [
        *subpackages('my.core'),
        *subpackages('my.kython'),
        'my.config',
        'my.cfg',
        'tests/misc.py',
        'tests/get_files.py',
        # 'tests/config.py', TODO hmm. unclear how to type check this module
    ]



def all_modules() -> Iterable[str]:
    yield from subpackages('my')
    yield from sorted(
        str(f.relative_to(DIR)) for f in (DIR / 'tests').rglob('*.py')
    )


def pylint():
    # TODO ugh. pylint still doesn't like checking my.config or my.books
    # only top level .py files seem ok??
    pass


def mypy(thing: str):
    is_package = Path(thing).suffix != '.py'
    cmd = [
        'mypy',
        '--color-output', # TODO eh? doesn't work..
        *(['-p'] if is_package else []), thing,
    ]
    print(' '.join(cmd), file=sys.stderr)
    return run(cmd, stdout=PIPE, stderr=PIPE)


def mypy_all() -> Iterable[Exception]:
    from concurrent.futures import ThreadPoolExecutor

    pkgs = list(core_modules() if CI else all_modules())
    log(f"Checking {pkgs}")
    with ThreadPoolExecutor() as pool:
        for p, res in zip(pkgs, pool.map(mypy, pkgs)):
            ret = res.returncode
            if ret > 0:
                log(f'FAILED: {p}')
            else:
                log(f'OK: {p}')
            print(res.stdout.decode('utf8'))
            print(res.stderr.decode('utf8'), file=sys.stderr)
            try:
                res.check_returncode()
            except Exception as e:
                yield e


def main():
    errors = list(mypy_all())
    if len(errors) > 0:
        sys.exit(1)


if __name__ == '__main__':
    main()
