#!/usr/bin/env python3
import glob
import io
import dataclasses
import pathlib
import sys
import subprocess
import os
import logging
import tomllib

from typing import (Self, Optional, Any,)

logger = logging.getLogger(__name__)

@dataclasses.dataclass
class PyProject:
    dependencies: dict[str, list[str]]
    early_features: Optional[list[str]] = None
    
def pyproject_load(
    d: pathlib.Path,
) -> PyProject:
    with io.open(d, 'rb') as f:
        content = tomllib.load(f)

    assert isinstance(content, dict)

    dependencies : dict[str, list[str]] = dict()

    dependencies['default'] = content['project']['dependencies']

    if (
        'optional-dependencies' in content['project']
    ):
        assert isinstance(
            content['project']['optional-dependencies'],
            dict
        )

        for k, v in content['project']['optional-dependencies'].items():
            assert isinstance(v, list)
            assert isinstance(k, str)

        dependencies[k] = v


    res = PyProject(
        dependencies=dependencies,
    )

    tool_name = 'online.fxreader.pr34'.replace('.', '-')

    if (
        'tool' in content and
        isinstance(
            content['tool'], dict
        ) and
        tool_name in content['tool'] and
        isinstance(
            content['tool'][tool_name],
            dict
        )
    ):
        if 'early_features' in content['tool'][tool_name]:
            res.early_features = content['tool'][tool_name]['early_features']

    return res

@dataclasses.dataclass
class BootstrapSettings:
    env_path: pathlib.Path
    python_path: pathlib.Path

    @classmethod
    def get(
        cls,
        base_dir: Optional[pathlib.Path] = None,
    ) -> Self:
        if base_dir is None:
            base_dir = pathlib.Path.cwd()

        env_path = base_dir / '.venv'
        python_path = env_path / 'bin' / 'python3'

        return cls(
            env_path=env_path,
            python_path=python_path,
        )

def env_bootstrap(
    bootstrap_settings: BootstrapSettings,
    pyproject: PyProject,
) -> None:
    subprocess.check_call([
        'uv', 'venv', '--seed',
        str(bootstrap_settings.env_path)
    ])

    subprocess.check_call([
        'uv',
        'pip',
        'install',
        '-p',
        bootstrap_settings.python_path,
        'uv',
    ])

    subprocess.check_call([
        bootstrap_settings.python_path,
        '-m',
        'uv', 'pip', 'install',
        'build', 'setuptools', 'meson-python', 'pybind11',
    ])

    early_wheels = glob.glob(
        str(
            pathlib.Path(__file__).parent / 'deps' / 'dist' / 'early' / '*.whl'
        )
    )

    if len(early_wheels) > 0:
        subprocess.check_call([
            bootstrap_settings.python_path,
            '-m',
            'uv', 'pip', 'install',
            *early_wheels,
        ])

    if pyproject.early_features:
        early_dependencies = sum([
            pyproject.dependencies[o]
            for o in pyproject.early_features
        ], [])

        logger.info(dict(
            early_dependencies=early_dependencies,
        ))
        if len(early_dependencies) > 0:
            subprocess.check_call([
                bootstrap_settings.python_path,
                '-m',
                'uv', 'pip', 'install',
                *early_dependencies,
            ])

def paths_equal(
    a: pathlib.Path | str,
    b: pathlib.Path | str
) -> bool:
    return (
        os.path.abspath(str(a))  ==
        os.path.abspath(str(b))
    )

def run(
    d: Optional[pathlib.Path] = None,
    cli_path: Optional[pathlib.Path] = None,
) -> None:
    if cli_path is None:
        cli_path = pathlib.Path(__file__).parent / 'cli.py'

    if d is None:
        d = pathlib.Path(__file__).parent / 'pyproject.toml'

    bootstrap_settings = BootstrapSettings.get()

    pyproject : PyProject = pyproject_load(
        d
    )

    logging.basicConfig(level=logging.INFO)

    if not bootstrap_settings.env_path.exists():
        env_bootstrap(
            bootstrap_settings=bootstrap_settings,
            pyproject=pyproject,
        )

    logger.info([sys.executable, sys.argv, bootstrap_settings.python_path])

    if not paths_equal(sys.executable, bootstrap_settings.python_path):
        os.execv(
            str(bootstrap_settings.python_path),
            [
                str(bootstrap_settings.python_path),
                *sys.argv,
            ]
        )

    os.execv(
        str(bootstrap_settings.python_path),
        [
            str(bootstrap_settings.python_path),
            str(
                cli_path
            ),
            *sys.argv[1:],
        ]
    )

if __name__ == '__main__':
    run(
        d=pathlib.Path(__file__).parent / 'python' / 'pyproject.toml',
        cli_path=pathlib.Path(__file__).parent / 'python' / 'cli.py',
    )