"""
/***************************************************************************
 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"
    PLUGIN_ID = "oqtopus"

    logsDirectory = ""
    _file_handler = None

    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/<PLUGIN_ID>
        On Linux: ~/.cache/<PLUGIN_ID>
        On Windows: C:/Users/<USER>/AppData/Local/<PLUGIN_ID>/cache
        """
        cache_dir = os.path.join(
            QStandardPaths.writableLocation(QStandardPaths.StandardLocation.CacheLocation),
            PluginUtils.PLUGIN_ID,
        )
        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),
            PluginUtils.PLUGIN_ID,
        )
        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)
            PluginUtils._file_handler = 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 shutdown_logger():
        """Close and remove the plugin's log file handler.

        On Windows, an open file handle on a log file located inside the
        plugin directory prevents QGIS from removing the plugin folder during
        uninstallation (see issue #84). Call this during plugin ``unload()``
        so that file handles are released before QGIS tries to delete files.
        """
        root_logger = logging.getLogger()
        # Remove the specific handler we registered
        handler = PluginUtils._file_handler
        if handler is not None:
            try:
                root_logger.removeHandler(handler)
                handler.close()
            except Exception:
                pass
            PluginUtils._file_handler = None

    @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 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)
