# ashyterm/settings/config.py

import os
import re
from functools import lru_cache
from pathlib import Path
from typing import Any, Dict

# Re-export from colors.py → API compat
from .colors import ColorSchemeMap, ColorSchemes

__all__ = ["ColorSchemes", "ColorSchemeMap"]

# Note: GTK/Pango imports are done lazily in functions that need them
# to avoid slow startup when only ConfigPaths is needed

try:
    from ..utils.exceptions import ConfigError, ErrorSeverity
    from ..utils.logger import get_logger
    from ..utils.platform import get_config_directory

    UTILS_AVAILABLE = True
except ImportError:
    UTILS_AVAILABLE = False
    get_config_directory = None  # type: ignore[assignment]


# Regex pattern for detecting common shell prompt terminators.
# Used to split the prompt from the user's command in VTE screen scraping.
# Includes: $ # % > ➜ ❯ ❮ › » ▶ λ ∴ ⟩ ⟫ ⮞ → ➤ ➔ ⇒
# These cover bash, zsh, fish, Oh My Zsh, Starship, Powerlevel10k, etc.
PROMPT_TERMINATOR_PATTERN = re.compile(r"[\$#%>➜❯ᐅ❮›»▶λ∴⟩⟫⮞→➤➔⇒]\s?")

# Configuration file names
_SESSIONS_FILENAME = "sessions.json"
_SETTINGS_FILENAME = "settings.json"
_STATE_FILENAME = "session_state.json"

# Font fallback constant
_FALLBACK_FONT = "Monospace 10"


class AppConstants:
    """Application metadata and identification constants."""

    APP_ID = "org.communitybig.ashyterm"
    APP_TITLE = "Ashy Terminal"
    APP_VERSION = "1.9.3"
    DEVELOPER_NAME = "BigCommunity"
    DEVELOPER_TEAM = ["BigCommunity Team"]
    COPYRIGHT = "© 2025 BigCommunity"
    WEBSITE = "https://communitybig.org/"
    ISSUE_URL = "https://github.com/big-comm/ashyterm/issues"


class ConfigPaths:
    """Platform-aware configuration paths for Linux."""

    def __init__(self):
        self.logger = get_logger("ashyterm.config.paths") if UTILS_AVAILABLE else None
        self._setup_paths()

    def _setup_paths(self):
        try:
            if UTILS_AVAILABLE and (config_dir := get_config_directory()):
                self.CONFIG_DIR = config_dir
            else:
                self.CONFIG_DIR = self._get_legacy_config_dir()

            self.CONFIG_DIR.mkdir(parents=True, exist_ok=True)
            try:
                self.CONFIG_DIR.chmod(0o700)
            except OSError:
                pass

            self.SESSIONS_FILE = self.CONFIG_DIR / _SESSIONS_FILENAME
            self.SETTINGS_FILE = self.CONFIG_DIR / _SETTINGS_FILENAME
            self.STATE_FILE = self.CONFIG_DIR / _STATE_FILENAME
            self.LAYOUT_DIR = self.CONFIG_DIR / "layouts"
            self.CACHE_DIR = self._get_cache_directory()
            self.LOG_DIR = self.CONFIG_DIR / "logs"
            self.BACKUP_DIR = (
                self.CONFIG_DIR / "backups"
            )  # Directory for manual backups

            for directory in [
                self.CACHE_DIR,
                self.LOG_DIR,
                self.LAYOUT_DIR,
                self.BACKUP_DIR,
            ]:
                try:
                    directory.mkdir(parents=True, exist_ok=True)
                except OSError as e:
                    if self.logger:
                        self.logger.warning(
                            f"Failed to create directory {directory}: {e}"
                        )
        except Exception as e:
            if self.logger:
                self.logger.error(f"Failed to initialize config paths: {e}")
            self._use_fallback_paths()

    def _get_legacy_config_dir(self) -> Path:
        if xdg_config := os.environ.get("XDG_CONFIG_HOME"):
            return Path(xdg_config) / "ashyterm"
        return Path.home() / ".config" / "ashyterm"

    def _get_cache_directory(self) -> Path:
        if xdg_cache := os.environ.get("XDG_CACHE_HOME"):
            return Path(xdg_cache) / "ashyterm"
        return Path.home() / ".cache" / "ashyterm"

    def _use_fallback_paths(self):
        home = Path.home()
        self.CONFIG_DIR = home / ".config" / "ashyterm"
        self.SESSIONS_FILE = self.CONFIG_DIR / _SESSIONS_FILENAME
        self.SETTINGS_FILE = self.CONFIG_DIR / _SETTINGS_FILENAME
        self.STATE_FILE = self.CONFIG_DIR / _STATE_FILENAME
        self.LAYOUT_DIR = self.CONFIG_DIR / "layouts"
        self.CACHE_DIR = home / ".cache" / "ashyterm"
        self.LOG_DIR = self.CONFIG_DIR / "logs"
        self.BACKUP_DIR = self.CONFIG_DIR / "backups"


class DefaultSettings:
    """Default application settings."""

    @staticmethod
    @lru_cache(maxsize=1)
    def get_available_default_font() -> str:
        """First available monospace font by priority; falls back to ``Monospace``."""
        # Font priority list: Nerd Fonts -> Popular distro fonts -> Universal fallback
        font_candidates = [
            "Noto Mono Nerd Font Medium 12",
            "JetBrains Mono 12",
            "Ubuntu Mono 12",
            "DejaVu Sans Mono 12",
            "Liberation Mono 12",
            "Source Code Pro 12",
            _FALLBACK_FONT,  # Generic fallback - always available
        ]

        try:
            # Lazy import GTK/Pango only when actually needed
            import gi

            gi.require_version("Pango", "1.0")
            # Get list of all available font families on the system
            import cairo
            from gi.repository import Pango, PangoCairo

            # Create a temporary surface to get font map
            surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, 1)
            context = cairo.Context(surface)
            pango_context = PangoCairo.create_context(context)
            font_map = pango_context.get_font_map()

            # Get all available font families
            available_families = set()
            for family in font_map.list_families():
                family_name = family.get_name().lower()
                available_families.add(family_name)

            # Test each candidate font
            for font_string in font_candidates:
                try:
                    font_desc = Pango.FontDescription.from_string(font_string)
                    family_name = font_desc.get_family()

                    if family_name:
                        # Check if font family exists in system
                        family_lower = family_name.lower()
                        if family_lower in available_families:
                            return font_string
                except Exception:
                    continue

            # Ultimate fallback if all checks fail
            return _FALLBACK_FONT

        except Exception:
            # If any import/context creation fails, return safe default
            return _FALLBACK_FONT

    # Category map for programmatic discoverability of settings groups.
    # Used by preferences UI and documentation.
    CATEGORIES: Dict[str, list] = {
        "appearance": [
            "gtk_theme",
            "color_scheme",
            "transparency",
            "headerbar_transparency",
            "font",
            "line_spacing",
            "bold_is_bright",
            "tab_alignment",
            "icon_theme_strategy",
        ],
        "window": [
            "window_width",
            "window_height",
            "window_maximized",
            "remember_window_state",
            "hide_headerbar_buttons_when_maximized",
        ],
        "behavior": [
            "sidebar_visible",
            "auto_hide_sidebar",
            "sidebar_width",
            "file_manager_height",
            "scroll_on_output",
            "scroll_on_keystroke",
            "scroll_on_insert",
            "mouse_autohide",
            "cursor_blink",
            "new_instance_behavior",
            "use_login_shell",
            "close_multiple_tabs_policy",
        ],
        "terminal": [
            "copy_on_select",
            "paste_warning_enabled",
            "scrollback_lines",
            "mouse_scroll_sensitivity",
            "touchpad_scroll_sensitivity",
            "cursor_shape",
            "bidi_enabled",
            "enable_shaping",
            "sixel_enabled",
            "text_blink_mode",
            "accessibility_enabled",
            "backspace_binding",
            "delete_binding",
            "cjk_ambiguous_width",
            "word_char_exceptions",
        ],
        "ssh": [
            "ssh_control_persist_duration",
        ],
        "logging": [
            "log_to_file",
            "console_log_level",
        ],
        "remote_editing": [
            "use_system_tmp_for_edit",
            "clear_remote_edit_files_on_exit",
        ],
        "ai": [
            "ai_assistant_enabled",
            "ai_assistant_provider",
            "ai_assistant_model",
            "ai_assistant_api_key",
            "ai_local_base_url",
            "ai_custom_quick_prompts",
        ],
        "search": [
            "search_case_sensitive",
            "search_use_regex",
        ],
        "highlighting": [
            "ignored_highlight_commands",
            "cat_colorization_enabled",
            "cat_theme_mode",
            "cat_dark_theme",
            "cat_light_theme",
            "pygments_theme",
            "shell_input_highlighting_enabled",
            "highlight_burst_threshold",
            "shell_input_theme_mode",
            "shell_input_dark_theme",
            "shell_input_light_theme",
            "shell_input_pygments_theme",
            "command_not_found_highlighting",
        ],
        "shortcuts": [
            "shortcuts",
        ],
    }

    @staticmethod
    @lru_cache(maxsize=1)
    def get_defaults() -> Dict[str, Any]:
        return {
            # General Appearance
            "gtk_theme": "terminal",
            "color_scheme": 1,
            "transparency": 16,
            "headerbar_transparency": 12,
            "font": DefaultSettings.get_available_default_font(),
            "line_spacing": 1.0,
            "bold_is_bright": False,
            "tab_alignment": "center",
            # Window State
            "window_width": 1200,
            "window_height": 700,
            "window_maximized": False,
            "remember_window_state": True,
            # Controls visibility of window control buttons when maximized
            # "auto" - detect from button-layout (hide if empty/only colon)
            # "always" - always hide buttons when maximized (for KDE Plasma Active Window Control)
            # "never" - never hide buttons (default GNOME-like behavior)
            "hide_headerbar_buttons_when_maximized": "auto",
            # Behavior
            "sidebar_visible": False,
            "auto_hide_sidebar": True,
            "sidebar_width": 300,  # Default sidebar width in pixels
            "file_manager_height": 250,  # Default file manager height in pixels
            "scroll_on_output": True,  # Enables smart scrolling
            "scroll_on_keystroke": True,
            "scroll_on_insert": True,  # Scroll to bottom on paste
            "copy_on_select": False,  # Auto-copy selection to clipboard
            "paste_warning_enabled": True,  # Confirm multi-line / risky paste
            "mouse_autohide": True,
            "cursor_blink": 0,
            "new_instance_behavior": "new_tab",
            "use_login_shell": False,
            "close_multiple_tabs_policy": "ask",  # ask, save_and_close, just_close
            # VTE Features
            "scrollback_lines": 10000,
            "mouse_scroll_sensitivity": 30.0,
            "touchpad_scroll_sensitivity": 30.0,
            "cursor_shape": 0,
            "bidi_enabled": False,
            "enable_shaping": False,  # For Arabic text shaping
            "sixel_enabled": False,
            "text_blink_mode": 0,
            "accessibility_enabled": True,
            # Compatibility & Advanced
            "backspace_binding": 0,
            "delete_binding": 0,
            "cjk_ambiguous_width": 1,
            "word_char_exceptions": "-_.:/~",  # For word selection on double-click
            "ssh_control_persist_duration": 60,  # Duration in seconds for SSH connection multiplexing
            # SSH host key verification: "ask" (prompt), "accept-new" (auto-add new hosts),
            # "yes" (reject unknown hosts), "no" (disable — DISCOURAGED).
            "ssh_strict_host_key_checking": "accept-new",
            "ssh_connect_timeout": 30,
            # Logging Settings
            "log_to_file": False,
            "console_log_level": "ERROR",
            # Remote Editing
            "use_system_tmp_for_edit": False,
            "clear_remote_edit_files_on_exit": True,
            # AI Assistant
            "ai_assistant_enabled": False,
            "ai_assistant_provider": "groq",
            "ai_assistant_model": "llama-3.1-8b-instant",
            "ai_assistant_api_key": "",
            "ai_local_base_url": "http://localhost:11434/v1",
            "ai_custom_quick_prompts": [],  # List of {"emoji": "🔧", "text": "prompt"}
            # Search Settings
            "search_case_sensitive": False,
            "search_use_regex": False,
            # Highlighting Settings
            "ignored_highlight_commands": [
                "grep",
                "egrep",
                "fgrep",
                "rg",
                "rga",
                "awk",
                "sed",
                "sd",
                "bat",
                "ls",
                "git",
                "vim",
                "nano",
                "nvim",
                "emacs",
                "htop",
                "btop",
                "top",
                "less",
                "more",
                "man",
                "info",
                "diff",
                "colordiff",
                "delta",
                "jq",
                "yq",
                "grc",
            ],
            # Cat Output Colorization Settings
            # When enabled, applies Pygments syntax highlighting to cat command output
            "cat_colorization_enabled": True,
            # Maximum bytes to highlight before falling back to raw output (in MB)
            "cat_highlight_limit_mb": 1,
            # Theme mode: "auto" (detects background), or "manual" (uses selected theme)
            "cat_theme_mode": "auto",
            # Pygments theme for dark backgrounds (used when mode is "auto" and bg is dark)
            "cat_dark_theme": "blinds-dark",
            # Pygments theme for light backgrounds (used when mode is "auto" and bg is light)
            "cat_light_theme": "blinds-light",
            # Legacy setting - now only used if mode is "manual"
            "pygments_theme": "monokai",
            # Shell Input Syntax Highlighting (experimental)
            # When enabled, applies Pygments syntax highlighting to shell commands as you type
            "shell_input_highlighting_enabled": False,
            # Burst detection threshold for syntax highlighting
            # Consecutive large chunks (>1KB) beyond this count disable highlighting
            "highlight_burst_threshold": 15,
            # Theme mode: "auto" (detects background), or "manual" (uses selected theme)
            "shell_input_theme_mode": "auto",
            # Pygments theme for dark backgrounds (used when mode is "auto" and bg is dark)
            "shell_input_dark_theme": "blinds-dark",
            # Pygments theme for light backgrounds (used when mode is "auto" and bg is light)
            "shell_input_light_theme": "blinds-light",
            # Legacy setting kept for backwards compatibility - now only used if mode is "manual"
            "shell_input_pygments_theme": "monokai",
            # Highlight unknown commands in red while typing
            # Requires shell_input_highlighting_enabled to be active
            "command_not_found_highlighting": True,
            # Icon Theme Strategy: "ashy" (bundled) or "system"
            # Using Ashy Icons by default speeds up GTK4 startup
            "icon_theme_strategy": "ashy",
            # Notification threshold in seconds for long-running commands (0 = disabled)
            "long_command_threshold": 30,
            # Shortcuts
            "shortcuts": {
                "new-local-tab": "<Control><Shift>t",
                "close-tab": "<Control><Shift>w",
                "copy": "<Control><Shift>c",
                "paste": "<Control><Shift>v",
                "select-all": "<Control><Shift>a",
                "clear-session": "<Control><Shift>l",
                "preferences": "<Control><Shift>comma",
                "quit": "<Control><Shift>q",
                "new-window": "<Control><Shift>n",
                "toggle-sidebar": "<Control><Shift>h",
                "show-command-manager": "<Control><Shift>m",
                "zoom-in": "<Control>plus",
                "zoom-out": "<Control>minus",
                "zoom-reset": "<Control>0",
                "split-horizontal": "<Control><Shift>o",
                "split-vertical": "<Control><Shift>d",
                "close-pane": "<Control><Shift>k",
                "zoom-pane": "<Control><Shift>z",
                "balance-panes": "<Control><Shift>equal",
                "next-tab": "<Control>Page_Down",
                "previous-tab": "<Control>Page_Up",
                "move-tab-left": "<Control><Shift>Page_Up",
                "move-tab-right": "<Control><Shift>Page_Down",
                "toggle-file-manager": "<Control><Shift>e",
                "toggle-search": "<Control><Shift>f",
                "toggle-broadcast": "<Control><Shift>b",
                "ai-assistant": "<Control><Shift>i",
            },
        }


_config_paths = None


def get_config_paths() -> ConfigPaths:
    """Get global configuration paths instance."""
    global _config_paths
    if _config_paths is None:
        _config_paths = ConfigPaths()
    return _config_paths


def initialize_configuration():
    """Initialize configuration system with validation."""
    logger = get_logger("ashyterm.config") if UTILS_AVAILABLE else None
    try:
        if logger:
            logger.info(
                f"Initializing {AppConstants.APP_TITLE} v{AppConstants.APP_VERSION}"
            )
        paths = get_config_paths()
        if logger:
            logger.info(f"Configuration directory: {paths.CONFIG_DIR}")
    except Exception as e:
        error_msg = f"Configuration initialization failed: {e}"
        if logger:
            logger.critical(error_msg)
        raise ConfigError(
            error_msg,
            severity=ErrorSeverity.CRITICAL,
            user_message="Application initialization failed",
        )


try:
    initialize_configuration()
except Exception as e:
    if UTILS_AVAILABLE:
        get_logger("ashyterm.settings.config").warning(
            f"Configuration initialization failed: {e}"
        )
    else:
        print(f"WARNING: Configuration initialization failed: {e}")

APP_ID = AppConstants.APP_ID
APP_TITLE = AppConstants.APP_TITLE
APP_VERSION = AppConstants.APP_VERSION
DEVELOPER_NAME = AppConstants.DEVELOPER_NAME
DEVELOPER_TEAM = AppConstants.DEVELOPER_TEAM
COPYRIGHT = AppConstants.COPYRIGHT
WEBSITE = AppConstants.WEBSITE
ISSUE_URL = AppConstants.ISSUE_URL

try:
    _paths = get_config_paths()
    CONFIG_DIR = str(_paths.CONFIG_DIR)
    SESSIONS_FILE = str(_paths.SESSIONS_FILE)
    SETTINGS_FILE = str(_paths.SETTINGS_FILE)
    STATE_FILE = str(_paths.STATE_FILE)
    LAYOUT_DIR = str(_paths.LAYOUT_DIR)
    BACKUP_DIR = str(_paths.BACKUP_DIR)
except Exception:
    CONFIG_DIR = os.path.expanduser("~/.config/ashyterm")
    SESSIONS_FILE = os.path.join(CONFIG_DIR, _SESSIONS_FILENAME)
    SETTINGS_FILE = os.path.join(CONFIG_DIR, _SETTINGS_FILENAME)
    STATE_FILE = os.path.join(CONFIG_DIR, _STATE_FILENAME)
    LAYOUT_DIR = os.path.join(CONFIG_DIR, "layouts")
    BACKUP_DIR = os.path.join(CONFIG_DIR, "backups")
    os.makedirs(CONFIG_DIR, exist_ok=True)
    os.makedirs(LAYOUT_DIR, exist_ok=True)
    os.makedirs(BACKUP_DIR, exist_ok=True)
