# built-in
from argparse import REMAINDER, ArgumentParser
from pathlib import Path

# app
from ..actions import get_lib_path, get_python, get_venv
from ..config import builders
from ..converters import CONVERTERS
from ..models import Requirement
from .base import BaseCommand


class ProjectRegisterCommand(BaseCommand):
    """Register a project in the system or in a venv.
    """
    @staticmethod
    def build_parser(parser) -> ArgumentParser:
        builders.build_config(parser)
        builders.build_from(parser)
        builders.build_output(parser)
        builders.build_venv(parser)
        builders.build_other(parser)
        parser.add_argument('name', nargs=REMAINDER, help='paths to install')
        return parser

    def __call__(self) -> bool:
        if 'from' not in self.config:
            self.logger.error('`--from` is required for this command')
            return False

        # no paths passed, register the current project globally
        if not self.args.name:
            project_path = Path(self.config['project'])
            self.logger.info('registering the project globally...', extra=dict(
                project_path=str(project_path),
            ))
            return self._register(project_path=project_path)

        # paths passed and venv for the current project exists,
        # register passed paths in the current venv.
        venv = get_venv(config=self.config)
        if venv.exists():
            self.logger.info('registering projects in the venv...', extra=dict(
                venv_path=str(venv.path),
            ))
            for path in self.args.name:
                path = Path(path)
                if not path.exists():
                    self.logger.error('project not found', extra=dict(path=str(path)))
                    return False
                ok = self._register(project_path=path, lib_path=venv.lib_path)
                if not ok:
                    return False
            return True

        # paths passed and no venv for the current project exists,
        # register passed paths globally.
        for path in self.args.name:
            path = Path(path)
            if not path.exists():
                self.logger.error('project not found', extra=dict(path=str(path)))
                return False
            self.logger.info('registering the project globally...', extra=dict(
                project_path=str(path),
            ))
            ok = self._register(project_path=path)
            if not ok:
                return False
        return True

    def _register(self, project_path: Path, lib_path: Path = None) -> bool:
        project_path = project_path.resolve()
        self._make_egg_info(
            project_path=project_path,
            from_format=self.config['from']['format'],
            from_path=self.config['from']['path'],
        )

        python = get_python(self.config)
        self.logger.debug('python found', extra=dict(python=str(python.path)))

        # get site-packages dir path
        if lib_path is None:
            lib_path = get_lib_path(python_path=python.path)
            if lib_path is None:
                self.logger.error('cannot find site-packages path', extra=dict(
                    python=str(python.path),
                ))
                return False
            self.logger.debug('lib found', extra=dict(python=str(lib_path)))

        # make egg-link
        self.logger.info('creating egg-link...')
        ok = self._upd_egg_link(lib_path=lib_path, project_path=project_path)
        if not ok:
            return False

        # update pth
        self._upd_pth(lib_path=lib_path, project_path=project_path)
        self.logger.info('registered!', extra=dict(python=str(python.path.name)))
        return True

    def _make_egg_info(self, project_path: Path, from_format: str, from_path: str) -> None:
        loader = CONVERTERS[from_format]
        loader = loader.copy(project_path=project_path)
        resolver = loader.load_resolver(path=from_path)
        if loader.lock:
            self.logger.warning('do not build project from lockfile!')

        # We don't attach deps here.
        # Use `deps convert` before to merge deps if you need it.
        # Please, open an issue if it is a case for you.

        # create egg-info
        reqs = Requirement.from_graph(resolver.graph, lock=False)
        self.logger.info('creating egg-info...')
        dumper = CONVERTERS['egginfo']
        dumper = dumper.copy(project_path=project_path)
        dumper.dump(
            path=project_path,
            reqs=reqs,
            project=resolver.graph.metainfo,
        )

    def _upd_egg_link(self, lib_path: Path, project_path: Path) -> bool:
        # find the correct one egg-info
        info_path = (project_path / project_path.name).with_suffix('.egg-info')
        if not info_path.exists():
            paths = list(project_path.glob('*.egg-info'))
            if len(paths) != 1:
                self.logger.warning('cannot find egg-info')
                return False
            info_path = paths[0]
        info_path = info_path.resolve()
        self.logger.debug('egg-info found', extra=dict(path=str(info_path)))

        # create egg-link
        link_path = (lib_path / info_path.name).with_suffix('.egg-link')
        link_path.write_text(str(info_path) + '\n.')
        self.logger.debug('egg-link created', extra=dict(path=str(link_path)))
        return True

    def _upd_pth(self, lib_path: Path, project_path: Path) -> None:
        # read existing content
        pth_path = lib_path / 'dephell.pth'
        content = ''
        if pth_path.exists():
            content = pth_path.read_text()

        # check if already added
        paths = content.splitlines()
        if str(project_path) in paths:
            self.logger.debug('already registered in pth', extra=dict(path=str(pth_path)))
            return

        # add
        project_path = project_path.resolve()
        content = content.rstrip() + '\n' + str(project_path) + '\n'
        pth_path.write_text(content.lstrip())
        self.logger.debug('pth updated', extra=dict(path=str(pth_path)))
