"""
/***************************************************************************
 Plugin Utils
                              -------------------
        begin                : 28.4.2018
        copyright            : (C) 2018 by OPENGIS.ch
        email                : matthias@opengis.ch
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""

import glob
import logging
import os
from datetime import datetime
from logging import LogRecord

from qgis.PyQt.QtCore import (
    QDir,
    QFileInfo,
    QObject,
    QSettings,
    QStandardPaths,
    QUrl,
    pyqtSignal,
)
from qgis.PyQt.QtGui import QColor, QDesktopServices, QIcon
from qgis.PyQt.uic import loadUiType

from ..libs.pum import SQL

logger = logging.getLogger("oqtopus")


class PluginUtils:

    PLUGIN_NAME = "oQtopus"

    logsDirectory = ""

    COLOR_GREEN = QColor(12, 167, 137)
    COLOR_WARNING = QColor(255, 165, 0)

    DOCUMENTATION_URL = "https://opengisch.github.io/oqtopus/"

    @staticmethod
    def plugin_root_path():
        """
        Returns the root path of the plugin
        """
        return os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))

    @staticmethod
    def plugin_cache_path():
        """Returns the persistent cache path for the plugin (GitHub API cache, downloaded packages).

        Uses CacheLocation which is appropriate for data that should persist
        across sessions but can be safely deleted.
        On macOS: ~/Library/Caches/oqtopus
        On Linux: ~/.cache/oqtopus
        On Windows: C:/Users/<USER>/AppData/Local/oqtopus/cache
        """
        cache_dir = os.path.join(
            QStandardPaths.writableLocation(QStandardPaths.StandardLocation.CacheLocation),
            "oqtopus",
        )
        os.makedirs(cache_dir, exist_ok=True)
        return cache_dir

    @staticmethod
    def plugin_temp_path():
        """Returns the temporary path for the plugin.

        DEPRECATED: Use plugin_cache_path() instead for persistent cache.
        This is kept for backward compatibility.
        """
        plugin_basename = PluginUtils.plugin_root_path().split(os.sep)[-1]

        plugin_temp_dir = os.path.join(
            QStandardPaths.writableLocation(QStandardPaths.StandardLocation.TempLocation),
            plugin_basename,
        )
        if not os.path.exists(plugin_temp_dir):
            os.makedirs(plugin_temp_dir)

        return plugin_temp_dir

    @staticmethod
    def get_all_cache_paths():
        """Returns all cache directories used by the plugin.

        This is used by the cleanup function to ensure all caches are deleted.
        """
        paths = []

        # Main cache directory (CacheLocation)
        cache_path = os.path.join(
            QStandardPaths.writableLocation(QStandardPaths.StandardLocation.CacheLocation),
            "oqtopus",
        )
        if os.path.exists(cache_path):
            paths.append(cache_path)

        # Legacy temp directory (TempLocation) - for backward compatibility
        plugin_basename = PluginUtils.plugin_root_path().split(os.sep)[-1]
        temp_path = os.path.join(
            QStandardPaths.writableLocation(QStandardPaths.StandardLocation.TempLocation),
            plugin_basename,
        )
        if os.path.exists(temp_path):
            paths.append(temp_path)

        return paths

    @staticmethod
    def get_plugin_icon_path(icon_filename):
        return os.path.join(PluginUtils.plugin_root_path(), "icons", icon_filename)

    @staticmethod
    def get_plugin_icon(icon_filename):
        return QIcon(PluginUtils.get_plugin_icon_path(icon_filename=icon_filename))

    @staticmethod
    def get_ui_class(ui_file):
        """Get UI Python class from .ui file.
           Can be filename.ui or subdirectory/filename.ui
        :param ui_file: The file of the ui in svir.ui
        :type ui_file: str
        """
        os.path.sep.join(ui_file.split("/"))
        ui_file_path = os.path.abspath(os.path.join(PluginUtils.plugin_root_path(), "ui", ui_file))
        return loadUiType(ui_file_path)[0]

    @staticmethod
    def get_metadata_file_path():
        return os.path.join(PluginUtils.plugin_root_path(), "metadata.txt")

    @staticmethod
    def get_plugin_version():
        ini_text = QSettings(PluginUtils.get_metadata_file_path(), QSettings.Format.IniFormat)
        return ini_text.value("version")

    @staticmethod
    def init_logger(logs_directory=None):
        if logs_directory is not None:
            PluginUtils.logsDirectory = logs_directory
        else:
            PluginUtils.logsDirectory = f"{PluginUtils.plugin_root_path()}/logs"

        directory = QDir(PluginUtils.logsDirectory)
        if not directory.exists():
            directory.mkpath(PluginUtils.logsDirectory)

        if directory.exists():
            # Create a new log file for each session with timestamp
            timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
            logfile = QFileInfo(directory, f"oQtopus_{timestamp}.log")
            PluginUtils.currentLogFile = logfile.filePath()

            # Clean up old log files, keep only the 10 most recent
            PluginUtils._cleanup_old_logs(PluginUtils.logsDirectory, keep=10)

            # Create file handler for this session
            fileHandler = logging.FileHandler(logfile.filePath(), mode="w")
            fileHandler.setLevel(SQL)  # Use SQL level (5) to capture all messages including SQL
            fileHandler.setFormatter(logging.Formatter("%(asctime)s %(levelname)-7s %(message)s"))

            # Configure logging - basicConfig might not work if already called
            # so we configure the root logger directly
            root_logger = logging.getLogger()
            root_logger.setLevel(SQL)  # Set root to SQL level to allow SQL messages through
            root_logger.addHandler(fileHandler)

            # Set the pum library to SQL level (5) to capture all SQL statements
            # SQL (5) < DEBUG (10) < INFO (20)
            logging.getLogger("pum").setLevel(SQL)
        else:
            logger.error(f"Can't create log files directory '{PluginUtils.logsDirectory}'.")

    @staticmethod
    def _cleanup_old_logs(logs_dir: str, keep: int = 10):
        """Remove old log files, keeping only the most recent ones.

        Args:
            logs_dir: Directory containing log files
            keep: Number of most recent log files to keep
        """
        log_files = glob.glob(os.path.join(logs_dir, "oQtopus_*.log"))
        # Sort by modification time, newest first
        log_files.sort(key=os.path.getmtime, reverse=True)
        # Remove old files beyond the keep limit
        for old_file in log_files[keep:]:
            try:
                os.remove(old_file)
            except OSError:
                pass  # Ignore errors when removing old logs

    @staticmethod
    def open_logs_folder():
        QDesktopServices.openUrl(QUrl.fromLocalFile(PluginUtils.logsDirectory))

    @staticmethod
    def open_log_file():
        log_file_path = getattr(PluginUtils, "currentLogFile", None)
        if log_file_path and os.path.exists(log_file_path):
            QDesktopServices.openUrl(QUrl.fromLocalFile(log_file_path))
        else:
            # Fallback: open most recent log file
            log_files = glob.glob(os.path.join(PluginUtils.logsDirectory, "oQtopus_*.log"))
            if log_files:
                log_files.sort(key=os.path.getmtime, reverse=True)
                QDesktopServices.openUrl(QUrl.fromLocalFile(log_files[0]))
            else:
                logger.error("No log file found.")

    @staticmethod
    def get_github_token():
        settings = QSettings()
        return settings.value("oqtopus/github_token", type=str)

    @staticmethod
    def set_github_token(token: str):
        settings = QSettings()
        settings.setValue("oqtopus/github_token", token)

    @staticmethod
    def get_allow_multiple_modules():
        settings = QSettings()
        return settings.value("oqtopus/allow_multiple_modules", False, type=bool)

    @staticmethod
    def set_allow_multiple_modules(allow: bool):
        settings = QSettings()
        settings.setValue("oqtopus/allow_multiple_modules", allow)

    @staticmethod
    def get_show_experimental_modules():
        settings = QSettings()
        return settings.value("oqtopus/show_experimental_modules", False, type=bool)

    @staticmethod
    def set_show_experimental_modules(show: bool):
        settings = QSettings()
        settings.setValue("oqtopus/show_experimental_modules", show)

    @staticmethod
    def get_log_show_datetime():
        settings = QSettings()
        return settings.value("oqtopus/log_show_datetime", True, type=bool)

    @staticmethod
    def set_log_show_datetime(show: bool):
        settings = QSettings()
        settings.setValue("oqtopus/log_show_datetime", show)

    @staticmethod
    def get_log_show_level():
        settings = QSettings()
        return settings.value("oqtopus/log_show_level", True, type=bool)

    @staticmethod
    def set_log_show_level(show: bool):
        settings = QSettings()
        settings.setValue("oqtopus/log_show_level", show)

    @staticmethod
    def get_log_show_module():
        settings = QSettings()
        return settings.value("oqtopus/log_show_module", True, type=bool)

    @staticmethod
    def set_log_show_module(show: bool):
        settings = QSettings()
        settings.setValue("oqtopus/log_show_module", show)

    @staticmethod
    def get_show_logs():
        settings = QSettings()
        return settings.value("oqtopus/show_logs", False, type=bool)

    @staticmethod
    def set_show_logs(show: bool):
        settings = QSettings()
        settings.setValue("oqtopus/show_logs", show)

    @staticmethod
    def get_github_headers():
        token = PluginUtils.get_github_token()
        headers = {}
        if token:
            headers["Authorization"] = f"token {token}"
        return headers

    @staticmethod
    def open_documentation():
        QDesktopServices.openUrl(QUrl(PluginUtils.DOCUMENTATION_URL))


class LoggingBridge(logging.Handler, QObject):

    loggedLine = pyqtSignal(LogRecord, str)

    def __init__(self, level=logging.NOTSET, excluded_modules=[]):
        QObject.__init__(self)
        logging.Handler.__init__(self, level)

        self.formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s")

        self.excluded_modules = excluded_modules

    def filter(self, record):
        return record.name not in self.excluded_modules

    def emit(self, record):
        log_entry = self.format(record)
        self.loggedLine.emit(record, log_entry)
