# -*- coding: utf-8 -*-

"""Installable PyCharm application descriptions."""

__all__ = ['PyCharmProApp', 'PyCharmCEApp']

import json
import os
import typing
import webbrowser
from anaconda_navigator import config as navigator_config
from anaconda_navigator.static import images
from . import base
from . import detectors

if typing.TYPE_CHECKING:
    import typing_extensions
    from anaconda_navigator.api import process
    from anaconda_navigator.config import user as user_config


MAC_APPS: typing.Optional[str] = detectors.join(
    detectors.Mac.home, 'Library', 'Application Support', 'JetBrains', 'Toolbox', 'apps',
)
LINUX_APPS: typing.Optional[str] = detectors.join(
    detectors.Linux.home, '.local', 'share', 'JetBrains', 'Toolbox', 'apps',
)
WIN_APPS: typing.Optional[str] = detectors.join(
    detectors.Win.local_app_data, 'JetBrains', 'Toolbox', 'apps',
)


class CheckVersion:
    """
    Detect version of the PyCharm application.

    :param product_code: acceptable product code(s)

                         Known options:

                         - PY: PyCharm Pro
                         - PC: PyCharm Community
                         - PE: PyCharm Edu
    :param configuration_path: Path to the `product-info.json` file relative to the root of the application.
    """

    __slots__ = ('__product_code', '__configuration_path')

    def __init__(
            self,
            product_code: typing.Union[str, typing.Iterable[str]],
            configuration_path: str = 'product-info.json',
    ) -> None:
        """Initialize new :class:`~CheckVersion` instance."""
        if isinstance(product_code, str):
            product_code = (product_code,)
        else:
            product_code = tuple(product_code)

        self.__product_code: 'typing_extensions.Final[typing.Tuple[str, ...]]' = product_code
        self.__configuration_path: 'typing_extensions.Final[str]' = configuration_path

    def __call__(
            self,
            parent: typing.Iterator[detectors.DetectedApplication],
    ) -> typing.Iterator[detectors.DetectedApplication]:
        """Iterate through detected applications."""
        application: detectors.DetectedApplication
        for application in parent:
            if not application.root:
                continue

            product_code: str
            version: str
            try:
                stream: typing.TextIO
                with open(os.path.join(application.root, self.__configuration_path), 'rt', encoding='utf-8') as stream:
                    data: typing.Mapping[str, typing.Any] = json.load(stream)

                product_code = data['productCode']
                version = data['version']
            except (OSError, ValueError, TypeError, AttributeError):
                continue

            if product_code in self.__product_code:
                yield application.replace(version=version)


class BasePyCharmApp(base.BaseInstallableApp):
    """Common parts for PyCharm applications."""

    def __init__(
            self,
            app_name: str,
            display_name: str,
            description: str,
            image_path: str,
            detector: 'detectors.Source',
            process_api: 'process.WorkerManager',
            config: 'user_config.UserConfig',
    ) -> None:
        """Initialize new :class:`~BasePyCharmApp` instance."""
        super().__init__(
            app_name=app_name,
            display_name=display_name,
            description=description,
            image_path=image_path,
            detector=detector,
            is_available=navigator_config.BITS_64,
            needs_license=False,
            process_api=process_api,
            config=config,
        )

    def install_extensions(self) -> 'process.ProcessWorker':
        """Install app extensions."""
        return self._process_api.create_process_worker(['python', '--version'])

    def update_config(self, prefix: str) -> None:
        """Update user config to use selected Python prefix interpreter."""


class PyCharmProApp(BasePyCharmApp):
    """PyCharm Professional application."""

    def __init__(self, process_api: 'process.WorkerManager', config: 'user_config.UserConfig') -> None:
        """Initialize new :class:`~PyCharmProApp` instance."""
        detector: 'typing_extensions.Final[detectors.Source]' = detectors.Group(
            detectors.Group(
                detectors.CheckConfiguredRoots('pycharm_pro_path', configuration=config),
                detectors.mac_only(),

                detectors.check_known_mac_roots('PyCharm.app'),
                detectors.Group(
                    detectors.CheckKnownRoots(detectors.join(MAC_APPS, 'PyCharm-P')),
                    detectors.StepIntoRoot(starts_with='ch-'),
                    detectors.StepIntoRoot(reverse=True),
                    detectors.StepIntoRoot(equals='PyCharm.app'),
                ),

                detectors.AppendExecutable(
                    os.path.join('Contents', 'MacOS', 'pycharm'),
                ),

                CheckVersion(
                    product_code='PY',
                    configuration_path=os.path.join('Contents', 'Resources', 'product-info.json'),
                ),
            ),
            detectors.Group(
                detectors.CheckConfiguredRoots('pycharm_pro_path', configuration=config),
                detectors.linux_only(),

                detectors.Group(
                    detectors.CheckKnownRoots(
                        detectors.join(detectors.Linux.root, 'opt'),
                    ),
                    detectors.StepIntoRoot(starts_with='pycharm', reverse=True),
                ),
                detectors.Group(
                    detectors.CheckKnownRoots(
                        detectors.join(LINUX_APPS, 'PyCharm-P'),
                    ),
                    detectors.StepIntoRoot(starts_with='ch-'),
                    detectors.StepIntoRoot(reverse=True),
                ),
                detectors.CheckKnownRoots(
                    detectors.join(detectors.Linux.root, 'snap', 'pycharm-professional', 'current'),
                    detectors.join(
                        detectors.Linux.root, 'var', 'lib', 'snapd', 'snap', 'pycharm-professional', 'current'
                    ),
                ),

                detectors.AppendExecutable(os.path.join('bin', 'pycharm.sh')),

                CheckVersion(
                    product_code='PY',
                ),
            ),
            detectors.Group(
                detectors.CheckConfiguredRoots('pycharm_pro_path', configuration=config),
                detectors.win_only(),

                detectors.Group(
                    detectors.CheckKnownRoots(
                        detectors.join(detectors.Win.program_files_x64, 'JetBrains'),
                        detectors.join(detectors.Win.program_files_x86, 'JetBrains'),
                    ),
                    detectors.StepIntoRoot(starts_with='PyCharm', reverse=True),
                ),
                detectors.Group(
                    detectors.CheckKnownRoots(
                        detectors.join(WIN_APPS, 'PyCharm-P'),
                    ),
                    detectors.StepIntoRoot(starts_with='ch-'),
                    detectors.StepIntoRoot(reverse=True),
                ),

                detectors.AppendExecutable(
                    os.path.join('bin', 'pycharm64.exe'),
                    os.path.join('bin', 'pycharm32.exe'),
                    os.path.join('bin', 'pycharm.exe'),
                ),

                CheckVersion(
                    product_code='PY',
                ),
            ),
        )

        super().__init__(
            app_name='pycharm_pro',
            display_name='PyCharm Professional',
            description=(
                'A full-fledged IDE by JetBrains for both Scientific and Web Python development. Supports HTML, JS, '
                'and SQL.'
            ),
            image_path=images.PYCHARM_ICON_1024_PATH,
            detector=detector,
            process_api=process_api,
            config=config,
        )

    def install(self) -> None:
        """Install application."""
        webbrowser.open_new_tab('https://www.anaconda.com/pycharm_navigator')


class PyCharmCEApp(BasePyCharmApp):
    """PyCharm Community Edition application."""

    def __init__(self, process_api: 'process.WorkerManager', config: 'user_config.UserConfig') -> None:
        """Initialize new :class:`~PyCharmCEApp` instance."""
        detector: 'typing_extensions.Final[detectors.Source]' = detectors.Group(
            detectors.Group(
                detectors.CheckConfiguredRoots('pycharm_pro_path', configuration=config),
                detectors.mac_only(),

                detectors.check_known_mac_roots('PyCharm CE.app', 'PyCharm Edu.app'),
                detectors.Group(
                    detectors.CheckKnownRoots(
                        detectors.join(MAC_APPS, 'PyCharm-C'),
                        detectors.join(MAC_APPS, 'PyCharm-E'),
                    ),
                    detectors.StepIntoRoot(starts_with='ch-'),
                    detectors.StepIntoRoot(reverse=True),
                    detectors.StepIntoRoot(equals=('PyCharm CE.app', 'PyCharm Edu.app')),
                ),

                detectors.AppendExecutable(
                    os.path.join('Contents', 'MacOS', 'pycharm'),
                ),

                CheckVersion(
                    product_code=('PC', 'PE'),
                    configuration_path=os.path.join('Contents', 'Resources', 'product-info.json'),
                ),
            ),
            detectors.Group(
                detectors.CheckConfiguredRoots('pycharm_pro_path', configuration=config),
                detectors.linux_only(),

                detectors.Group(
                    detectors.CheckKnownRoots(
                        detectors.join(detectors.Linux.root, 'opt'),
                    ),
                    detectors.StepIntoRoot(starts_with='pycharm', reverse=True),
                ),
                detectors.Group(
                    detectors.CheckKnownRoots(
                        detectors.join(LINUX_APPS, 'PyCharm-C'),
                        detectors.join(LINUX_APPS, 'PyCharm-E'),
                    ),
                    detectors.StepIntoRoot(starts_with='ch-'),
                    detectors.StepIntoRoot(reverse=True),
                ),
                detectors.CheckKnownRoots(
                    detectors.join(detectors.Linux.root, 'snap', 'pycharm-community', 'current'),
                    detectors.join(
                        detectors.Linux.root, 'var', 'lib', 'snapd', 'snap', 'pycharm-community', 'current'
                    ),
                    detectors.join(detectors.Linux.root, 'snap', 'pycharm-educational', 'current'),
                    detectors.join(
                        detectors.Linux.root, 'var', 'lib', 'snapd', 'snap', 'pycharm-educational', 'current'
                    ),
                ),

                detectors.AppendExecutable(os.path.join('bin', 'pycharm.sh')),

                CheckVersion(
                    product_code=('PC', 'PE'),
                ),
            ),
            detectors.Group(
                detectors.CheckConfiguredRoots('pycharm_pro_path', configuration=config),
                detectors.win_only(),

                detectors.Group(
                    detectors.CheckKnownRoots(
                        detectors.join(detectors.Win.program_files_x64, 'JetBrains'),
                        detectors.join(detectors.Win.program_files_x86, 'JetBrains'),
                    ),
                    detectors.StepIntoRoot(starts_with='PyCharm', reverse=True),
                ),
                detectors.Group(
                    detectors.CheckKnownRoots(
                        detectors.join(WIN_APPS, 'PyCharm-C'),
                        detectors.join(WIN_APPS, 'PyCharm-E'),
                    ),
                    detectors.StepIntoRoot(starts_with='ch-'),
                    detectors.StepIntoRoot(reverse=True),
                ),

                detectors.AppendExecutable(
                    os.path.join('bin', 'pycharm64.exe'),
                    os.path.join('bin', 'pycharm32.exe'),
                    os.path.join('bin', 'pycharm.exe'),
                ),

                CheckVersion(
                    product_code=('PC', 'PE'),
                ),
            ),
        )

        super().__init__(
            app_name='pycharm_ce',
            display_name='PyCharm Community',
            description=(
                'An IDE by JetBrains for pure Python development. Supports code completion, listing, and debugging.'
            ),
            image_path=images.PYCHARM_CE_ICON_1024_PATH,
            detector=detector,
            process_api=process_api,
            config=config,
        )
