"""Persistence for metrics configuration and alert rules."""

from __future__ import annotations

import os
import stat
from pathlib import Path

import yaml

from gaol.config import get_data_dir
from gaol.metrics.exceptions import AlertRuleExistsError, AlertRuleNotFoundError
from gaol.metrics.models import AlertRule, MetricsServerConfig


def _get_metrics_dir() -> Path:
    """Get the metrics data directory."""
    return get_data_dir() / "metrics"


class MetricsStore:
    """Persistent storage for metrics configuration and alert rules.

    Directory structure:
        ~/.local/share/gaol/metrics/
            config.yaml          # MetricsServerConfig
            alerts/
                rules.yaml       # List of AlertRule
    """

    def __init__(self, store_dir: Path | None = None) -> None:
        """Initialize the store.

        Args:
            store_dir: Optional override for the store directory
        """
        self._store_dir = store_dir or _get_metrics_dir()
        self._alerts_dir = self._store_dir / "alerts"

    def _ensure_dirs(self) -> None:
        """Ensure store directories exist with proper permissions."""
        for dir_path in [self._store_dir, self._alerts_dir]:
            dir_path.mkdir(parents=True, exist_ok=True)
            os.chmod(dir_path, stat.S_IRWXU)  # 700

    def _write_yaml(self, path: Path, data: dict | list) -> None:
        """Write YAML with atomic write and proper permissions."""
        self._ensure_dirs()
        path.parent.mkdir(parents=True, exist_ok=True)
        temp_path = path.with_suffix(".tmp")
        with open(temp_path, "w") as f:
            yaml.dump(data, f, default_flow_style=False)
        os.chmod(temp_path, stat.S_IRUSR | stat.S_IWUSR)  # 600
        temp_path.rename(path)

    def _read_yaml(self, path: Path) -> dict | list | None:
        """Read YAML file if it exists."""
        if not path.exists():
            return None
        with open(path) as f:
            return yaml.safe_load(f)

    # -------------------------------------------------------------------------
    # Server Configuration
    # -------------------------------------------------------------------------

    def save_config(self, config: MetricsServerConfig) -> None:
        """Save metrics server configuration."""
        path = self._store_dir / "config.yaml"
        self._write_yaml(path, config.model_dump(mode="json"))

    def get_config(self) -> MetricsServerConfig:
        """Get metrics server configuration.

        Returns default config if none saved.
        """
        path = self._store_dir / "config.yaml"
        data = self._read_yaml(path)
        if data is None or not isinstance(data, dict):
            return MetricsServerConfig()
        return MetricsServerConfig.model_validate(data)

    def delete_config(self) -> bool:
        """Delete server configuration. Returns True if deleted."""
        path = self._store_dir / "config.yaml"
        if path.exists():
            path.unlink()
            return True
        return False

    # -------------------------------------------------------------------------
    # Alert Rules
    # -------------------------------------------------------------------------

    def _get_rules_path(self) -> Path:
        """Get path to the rules file."""
        return self._alerts_dir / "rules.yaml"

    def save_rules(self, rules: list[AlertRule]) -> None:
        """Save all alert rules."""
        path = self._get_rules_path()
        data = [rule.model_dump(mode="json") for rule in rules]
        self._write_yaml(path, data)

    def get_rules(self) -> list[AlertRule]:
        """Get all alert rules."""
        path = self._get_rules_path()
        data = self._read_yaml(path)
        if data is None or not isinstance(data, list):
            return []
        return [AlertRule.model_validate(item) for item in data]

    def get_rule(self, name: str) -> AlertRule | None:
        """Get a specific alert rule by name."""
        rules = self.get_rules()
        for rule in rules:
            if rule.name == name:
                return rule
        return None

    def add_rule(self, rule: AlertRule) -> None:
        """Add a new alert rule.

        Raises:
            AlertRuleExistsError: If rule with same name exists
        """
        rules = self.get_rules()
        for existing in rules:
            if existing.name == rule.name:
                raise AlertRuleExistsError(rule.name)
        rules.append(rule)
        self.save_rules(rules)

    def update_rule(self, rule: AlertRule) -> None:
        """Update an existing alert rule.

        Raises:
            AlertRuleNotFoundError: If rule doesn't exist
        """
        rules = self.get_rules()
        found = False
        for i, existing in enumerate(rules):
            if existing.name == rule.name:
                rules[i] = rule
                found = True
                break

        if not found:
            raise AlertRuleNotFoundError(rule.name)

        self.save_rules(rules)

    def delete_rule(self, name: str) -> bool:
        """Delete an alert rule by name. Returns True if deleted."""
        rules = self.get_rules()
        original_count = len(rules)
        rules = [r for r in rules if r.name != name]

        if len(rules) < original_count:
            self.save_rules(rules)
            return True
        return False

    def rule_exists(self, name: str) -> bool:
        """Check if an alert rule exists."""
        return self.get_rule(name) is not None

    def get_enabled_rules(self) -> list[AlertRule]:
        """Get all enabled alert rules."""
        return [rule for rule in self.get_rules() if rule.enabled]

    def enable_rule(self, name: str) -> bool:
        """Enable an alert rule. Returns True if rule was found and enabled."""
        rule = self.get_rule(name)
        if rule is None:
            return False
        if not rule.enabled:
            rule.enabled = True
            self.update_rule(rule)
        return True

    def disable_rule(self, name: str) -> bool:
        """Disable an alert rule. Returns True if rule was found and disabled."""
        rule = self.get_rule(name)
        if rule is None:
            return False
        if rule.enabled:
            rule.enabled = False
            self.update_rule(rule)
        return True


# Global instance
_store: MetricsStore | None = None


def get_metrics_store() -> MetricsStore:
    """Get the global metrics store instance."""
    global _store
    if _store is None:
        _store = MetricsStore()
    return _store
