# SPDX-License-Identifier: LGPL-2.1-or-later
# SPDX-FileCopyrightText: 2022 FreeCAD Project Association
# SPDX-FileNotice: Part of the AddonManager.

################################################################################
#                                                                              #
#   This addon is free software: you can redistribute it and/or modify         #
#   it under the terms of the GNU Lesser General Public License as             #
#   published by the Free Software Foundation, either version 2.1              #
#   of the License, or (at your option) any later version.                     #
#                                                                              #
#   This addon is distributed in the hope that it will be useful,              #
#   but WITHOUT ANY WARRANTY; without even the implied warranty                #
#   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                    #
#   See the GNU Lesser General Public License for more details.                #
#                                                                              #
#   You should have received a copy of the GNU Lesser General Public           #
#   License along with this addon. If not, see https://www.gnu.org/licenses    #
#                                                                              #
################################################################################

from dataclasses import dataclass
from enum import Enum, auto
import os
from typing import Optional

from addonmanager_freecad_interface import translate

from PySideWrapper import QtCore, QtWidgets

from .addonmanager_widget_addon_buttons import WidgetAddonButtons
from .addonmanager_widget_readme_browser import WidgetReadmeBrowser
from .addonmanager_colors import (
    warning_color_string,
    attention_color_string,
    bright_color_string,
)


class MessageType(Enum):
    Message = auto()
    Warning = auto()
    Error = auto()


@dataclass
class UpdateInformation:
    unchecked: bool = True
    check_in_progress: bool = False
    update_available: bool = False
    detached_head: bool = False
    version: str = ""
    tag: str = ""
    branch: Optional[str] = None


@dataclass
class WarningFlags:
    obsolete: bool = False
    python2: bool = False
    required_freecad_version: Optional[str] = None
    non_osi_approved: bool = False
    non_fsf_libre: bool = False


class PackageDetailsView(QtWidgets.QWidget):
    """The view class for the package details"""

    def __init__(self, parent: QtWidgets.QWidget = None):
        super().__init__(parent)
        self.button_bar = None
        self.readme_browser = None
        self.message_label = None
        self.location_label = None
        self.url_label = None
        self.installed = False
        self.disabled = False
        self.update_info = UpdateInformation()
        self.warning_flags = WarningFlags()
        self.installed_version = None
        self.installed_branch = None
        self.installed_timestamp = None
        self.can_disable = True
        self._setup_ui()

    def _setup_ui(self):
        self.vertical_layout = QtWidgets.QVBoxLayout(self)
        self.button_bar = WidgetAddonButtons(self)
        self.readme_browser = WidgetReadmeBrowser(self)
        self.message_label = QtWidgets.QLabel(self)
        self.location_label = QtWidgets.QLabel(self)
        self.url_label = QtWidgets.QLabel(self)
        self.url_label.setOpenExternalLinks(True)
        self.location_label.setOpenExternalLinks(True)
        self.vertical_layout.addWidget(self.button_bar)
        self.vertical_layout.addWidget(self.message_label)
        self.vertical_layout.addWidget(self.location_label)
        self.vertical_layout.addWidget(self.url_label)
        self.vertical_layout.addWidget(self.readme_browser)
        self.button_bar.hide()  # Start with no bar

    def set_location(self, location: Optional[str]):
        if location is not None:
            location_path = os.path.normpath(location)
            text = (
                f"{translate('AddonsInstaller', 'Installation location')}: "
                f'<a href="file://{location_path}">{location_path}</a>'
            )
            self.location_label.setText(text)
            self.location_label.show()
        else:
            self.location_label.hide()

    def set_url(self, url: Optional[str]):
        if url is not None:
            text = (
                translate("AddonsInstaller", "Repository URL")
                + ': <a href="'
                + url
                + '">'
                + url
                + "</a>"
            )
            self.url_label.setText(text)
            self.url_label.show()
        else:
            self.url_label.hide()

    def set_installed(
        self,
        installed: bool,
        on_date: float = None,
        version: Optional[str] = None,
        branch: Optional[str] = None,
    ):
        self.installed = installed
        self.installed_timestamp = on_date
        self.installed_version = version
        self.installed_branch = branch
        if not self.installed:
            self.set_location(None)
        self._sync_ui_state()

    def set_update_available(self, info: UpdateInformation):
        self.update_info = info
        self._sync_ui_state()

    def set_disabled(self, disabled: bool):
        self.disabled = disabled
        self._sync_ui_state()

    def allow_disabling(self, allow: bool):
        self.can_disable = allow
        self._sync_ui_state()

    def allow_running(self, show: bool):
        self.button_bar.run_macro.setVisible(show)

    def set_warning_flags(self, flags: WarningFlags):
        self.warning_flags = flags
        self._sync_ui_state()

    def set_new_disabled_status(self, disabled: bool):
        """If the user just changed the enabled/disabled state of the addon, display a message
        indicating that will not take place until restart. Do not call except in a case of a
        state change during this run."""

        if disabled:
            message = translate(
                "AddonsInstaller",
                "This addon will be disabled when restarting FreeCAD",
            )
        else:
            message = translate(
                "AddonsInstaller",
                "This addon will be enabled when restarting FreeCAD",
            )
        self.message_label.setText(f"<h3>{message}</h3>")
        self.message_label.setStyleSheet("color:" + attention_color_string())

    def set_new_branch(self, branch: str):
        """If the user just changed branches, update the message to show that a restart is
        needed."""
        message_string = "<h3>"
        message_string += translate(
            "AddonsInstaller", "Changed to branch '{}' -- restart FreeCAD to use the addon"
        ).format(branch)
        message_string += "</h3>"
        self.message_label.setText(message_string)
        self.message_label.setStyleSheet("color:" + attention_color_string())

    def set_updated(self):
        """If the user has just updated the addon but not yet restarted, show an indication that
        we are awaiting a restart."""
        message = translate(
            "AddonsInstaller",
            "This addon has been updated. Restart FreeCAD to see changes.",
        )
        self.message_label.setText(f"<h3>{message}</h3>")
        self.message_label.setStyleSheet("color:" + attention_color_string())

    def _sync_ui_state(self):
        self._create_status_label_text()

    def _create_status_label_text(self):
        if self.installed:
            installation_details = self._get_installation_details_string()
            update_details = self._get_update_status_string()
            message_text = f"{installation_details} {update_details}"
            if self.disabled:
                message_text += " [" + translate("AddonsInstaller", "Disabled") + "]"
            self.message_label.setText(f"<h3>{message_text}</h3>")
            if self.disabled:
                self.message_label.setStyleSheet("color:" + warning_color_string())
            elif self.update_info.update_available:
                self.message_label.setStyleSheet("color:" + attention_color_string())
            else:
                self.message_label.setStyleSheet("color:" + bright_color_string())
            self.message_label.show()
        elif self._there_are_warnings_to_show():
            warnings = self._get_warning_string()
            self.message_label.setText(f"<h3>{warnings}</h3>")
            self.message_label.setStyleSheet("color:" + warning_color_string())
            self.message_label.show()
        else:
            self.message_label.hide()

    def _get_installation_details_string(self) -> str:
        version = self.installed_version
        date = ""
        installed_version_string = ""
        if self.installed_timestamp:
            date = QtCore.QLocale().toString(
                QtCore.QDateTime.fromSecsSinceEpoch(int(round(self.installed_timestamp, 0))),
                QtCore.QLocale.ShortFormat,
            )
        if version and date:
            installed_version_string += (
                translate("AddonsInstaller", "Version {version} installed on {date}").format(
                    version=version, date=date
                )
                + ". "
            )
        elif version:
            installed_version_string += (
                translate("AddonsInstaller", "Version {version} installed") + "."
            ).format(version=version)
        elif date:
            installed_version_string += (
                translate("AddonsInstaller", "Installed on {date}") + "."
            ).format(date=date)
        else:
            installed_version_string += translate("AddonsInstaller", "Installed") + "."
        return installed_version_string

    def _get_update_status_string(self) -> str:
        if self.update_info.check_in_progress:
            return translate("AddonsInstaller", "Update check in progress") + "."
        elif self.update_info.unchecked:
            return ""
        if self.update_info.detached_head:
            return (
                translate(
                    "AddonsInstaller", "Git tag '{}' checked out, no updates possible"
                ).format(self.update_info.tag)
                + "."
            )
        if self.update_info.update_available:
            if self.installed_branch and self.update_info.branch:
                if self.installed_branch != self.update_info.branch:
                    return (
                        translate(
                            "AddonsInstaller",
                            "Currently on branch {}, name changed to {}",
                        ).format(self.installed_branch, self.update_info.branch)
                        + "."
                    )
                if self.update_info.version:
                    return (
                        translate(
                            "AddonsInstaller",
                            "Currently on branch {}, update available to version {}",
                        ).format(self.installed_branch, str(self.update_info.version).strip())
                        + "."
                    )
                return translate("AddonsInstaller", "Update available") + "."
            if self.update_info.version:
                return (
                    translate("AddonsInstaller", "Update available to version {}").format(
                        str(self.update_info.version).strip()
                    )
                    + "."
                )
            return translate("AddonsInstaller", "Update available") + "."
        return translate("AddonsInstaller", "This is the latest version available") + "."

    def _there_are_warnings_to_show(self) -> bool:
        if self.disabled:
            return True
        if self.warning_flags.required_freecad_version:
            return True
        return False  # TODO: Someday support optional warnings on license types

    def _get_warning_string(self) -> str:
        if self.installed and self.disabled:
            return translate(
                "AddonsInstaller",
                "WARNING: This addon is currently installed, but disabled. Use the 'enable' "
                "button to re-enable.",
            )
        if self.warning_flags.required_freecad_version:
            return translate("AddonsInstaller", "WARNING: This addon requires FreeCAD {}").format(
                self.warning_flags.required_freecad_version
            )
        return ""
