# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
#
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)

"""Status bar widget."""

# Standard library imports
import os
import os.path as osp
import sys

# Third party imports
from qtpy.QtCore import QPoint, QTimer, Signal
from qtpy.QtWidgets import QMenu

# Local imports
from spyder.api.translations import get_translation
from spyder.api.widgets.status import BaseTimerStatus
from spyder.utils.qthelpers import add_actions, create_action
from spyder.utils.conda import get_list_conda_envs
from spyder.utils.programs import get_interpreter_info
from spyder.utils.pyenv import get_list_pyenv_envs
from spyder.utils.workers import WorkerManager


# Localization
_ = get_translation('spyder')


class InterpreterStatus(BaseTimerStatus):
    """Status bar widget for displaying the current conda environment."""
    ID = 'interpreter_status'

    DEFAULT_OPTIONS = {
        'default': True,
        'custom': False,
    }

    sig_open_preferences_requested = Signal()
    """
    Signal to open the main interpreter preferences.
    """

    def __init__(self, parent, icon=None, interpreter=None):
        """Status bar widget for displaying the current conda environment."""
        self._interpreter = interpreter
        super().__init__(parent)
        self.main = parent
        self.env_actions = []
        self.path_to_env = {}
        self.envs = {}
        self.value = ''

        self.menu = QMenu(self)
        self.sig_clicked.connect(self.show_menu)

        # Worker to compute envs in a thread
        self._worker_manager = WorkerManager(max_threads=1)

        # Timer to get envs every minute
        self._get_envs_timer = QTimer(self)
        self._get_envs_timer.setInterval(60000)
        self._get_envs_timer.timeout.connect(self.get_envs)
        self._get_envs_timer.start()

        # Update the list of envs at startup
        self.get_envs()

    def on_option_update(self, option, value):
        pass

    # ---- BaseTimerStatus API
    def get_value(self):
        """
        Switch to default interpreter if current env was removed or
        update Python version of current one.
        """
        env_dir = self._get_env_dir(self._interpreter)

        if not osp.isdir(env_dir):
            # Env was removed on Mac or Linux
            self.set_option('custom', False)
            self.set_option('default', True)
            self.update_interpreter(sys.executable)
        elif not osp.isfile(self._interpreter):
            # This can happen on Windows because the interpreter was
            # renamed to .conda_trash
            if not osp.isdir(osp.join(env_dir, 'conda-meta')):
                # If conda-meta is missing, it means the env was removed
                self.set_option('custom', False)
                self.set_option('default', True)
                self.update_interpreter(sys.executable)
            else:
                # If not, it means the interpreter is being updated so
                # we need to update its version
                self.get_envs()
        else:
            # We need to do this in case the Python version was
            # changed in the env
            if self._interpreter in self.path_to_env:
                self.update_interpreter()

        return self.value

    def get_tooltip(self):
        """Override api method."""
        return self._interpreter if self._interpreter else ''

    def get_icon(self):
        return self.create_icon('environment')

    # ---- Widget API
    def _get_env_dir(self, interpreter):
        """Get env directory from interpreter executable."""
        if os.name == 'nt':
            return osp.dirname(interpreter)
        else:
            return osp.dirname(osp.dirname(interpreter))

    def _get_envs(self):
        """Get the list of environments in the system."""
        # Compute info of default interpreter to have it available in
        # case we need to switch to it. This will avoid lags when
        # doing that in get_value.
        if sys.executable not in self.path_to_env:
            self._get_env_info(sys.executable)

        # Get envs
        conda_env = get_list_conda_envs()
        pyenv_env = get_list_pyenv_envs()
        return {**conda_env, **pyenv_env}

    def get_envs(self):
        """
        Get the list of environments in a thread to keep them up to
        date.
        """
        self._worker_manager.terminate_all()
        worker = self._worker_manager.create_python_worker(self._get_envs)
        worker.sig_finished.connect(self.update_envs)
        worker.start()

    def update_envs(self, worker, output, error):
        """Update the list of environments in the system."""
        self.envs.update(**output)
        for env in list(self.envs.keys()):
            path, version = self.envs[env]
            # Save paths in lowercase on Windows to avoid issues with
            # capitalization.
            path = path.lower() if os.name == 'nt' else path
            self.path_to_env[path] = env

        self.update_interpreter()

    def open_interpreter_preferences(self):
        """Request to open the main interpreter preferences."""
        self.sig_open_preferences_requested.emit()

    def show_menu(self):
        """Display a menu when clicking on the widget."""
        menu = self.menu
        menu.clear()
        text = _("Change default environment in Preferences...")
        change_action = create_action(
            self,
            text=text,
            triggered=self.open_interpreter_preferences,
        )
        add_actions(menu, [change_action])
        rect = self.contentsRect()
        os_height = 7 if os.name == 'nt' else 12
        pos = self.mapToGlobal(
                rect.topLeft() + QPoint(-40, -rect.height() - os_height))
        menu.popup(pos)

    def _get_env_info(self, path):
        """Get environment information."""
        path = path.lower() if os.name == 'nt' else path
        try:
            name = self.path_to_env[path]
        except KeyError:
            win_app_path = osp.join(
                'AppData', 'Local', 'Programs', 'spyder')
            if 'Spyder.app' in path or win_app_path in path:
                name = 'internal'
            elif 'conda' in path:
                name = 'conda'
            elif 'pyenv' in path:
                name = 'pyenv'
            else:
                name = 'custom'
            version = get_interpreter_info(path)
            self.path_to_env[path] = name
            self.envs[name] = (path, version)
        __, version = self.envs[name]
        return f'{name} ({version})'

    def update_interpreter(self, interpreter=None):
        """Set main interpreter and update information."""
        if interpreter:
            self._interpreter = interpreter
        self.value = self._get_env_info(self._interpreter)
        self.set_value(self.value)
        self.update_tooltip()
