"""Persistence for backup policies, history, and metadata."""

from __future__ import annotations

import os
import stat
from pathlib import Path

import yaml

from gaol.backup.models import (
    BackupHistory,
    BackupMetadata,
    BackupPolicy,
    GlobalBackupConfig,
)
from gaol.config import get_data_dir


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


class BackupStore:
    """Persistent storage for backup policies, history, and metadata.

    Directory structure:
        ~/.local/share/gaol/backup/
            config.yaml           # Global backup config
            policies/
                {container_name}.yaml
            history/
                {container_name}.yaml
            metadata/
                {container_name}/
                    {backup_id}.yaml
    """

    def __init__(self, store_dir: Path | None = None) -> None:
        """Initialize the store."""
        self._store_dir = store_dir or _get_backup_dir()
        self._config_file = self._store_dir / "config.yaml"
        self._policies_dir = self._store_dir / "policies"
        self._history_dir = self._store_dir / "history"
        self._metadata_dir = self._store_dir / "metadata"

    def _ensure_dirs(self) -> None:
        """Ensure store directories exist with proper permissions."""
        for dir_path in [
            self._store_dir,
            self._policies_dir,
            self._history_dir,
            self._metadata_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) -> 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 | None:
        """Read YAML file if it exists."""
        if not path.exists():
            return None
        with open(path) as f:
            return yaml.safe_load(f) or {}

    # -------------------------------------------------------------------------
    # Global config operations
    # -------------------------------------------------------------------------

    def get_global_config(self) -> GlobalBackupConfig:
        """Get global backup configuration."""
        data = self._read_yaml(self._config_file)
        if data is None:
            return GlobalBackupConfig()
        return GlobalBackupConfig.model_validate(data)

    def save_global_config(self, config: GlobalBackupConfig) -> None:
        """Save global backup configuration."""
        self._write_yaml(self._config_file, config.model_dump(mode="json"))

    # -------------------------------------------------------------------------
    # Policy operations
    # -------------------------------------------------------------------------

    def save_policy(self, policy: BackupPolicy) -> None:
        """Save or update a backup policy."""
        path = self._policies_dir / f"{policy.container_name}.yaml"
        self._write_yaml(path, policy.model_dump(mode="json"))

    def get_policy(self, container_name: str) -> BackupPolicy | None:
        """Get backup policy for a container."""
        path = self._policies_dir / f"{container_name}.yaml"
        data = self._read_yaml(path)
        if data is None:
            return None
        return BackupPolicy.model_validate(data)

    def delete_policy(self, container_name: str) -> bool:
        """Delete a policy. Returns True if deleted."""
        path = self._policies_dir / f"{container_name}.yaml"
        if path.exists():
            path.unlink()
            return True
        return False

    def list_policies(self) -> list[BackupPolicy]:
        """List all backup policies."""
        if not self._policies_dir.exists():
            return []

        policies = []
        for path in self._policies_dir.glob("*.yaml"):
            try:
                data = self._read_yaml(path)
                if data:
                    policies.append(BackupPolicy.model_validate(data))
            except Exception:
                continue
        return policies

    def policy_exists(self, container_name: str) -> bool:
        """Check if a policy exists."""
        path = self._policies_dir / f"{container_name}.yaml"
        return path.exists()

    # -------------------------------------------------------------------------
    # History operations
    # -------------------------------------------------------------------------

    def save_history(self, history: BackupHistory) -> None:
        """Save backup history for a container."""
        path = self._history_dir / f"{history.container_name}.yaml"
        self._write_yaml(path, history.model_dump(mode="json"))

    def get_history(self, container_name: str) -> BackupHistory:
        """Get backup history for a container (creates if not exists)."""
        path = self._history_dir / f"{container_name}.yaml"
        data = self._read_yaml(path)
        if data is None:
            return BackupHistory(container_name=container_name)
        return BackupHistory.model_validate(data)

    def delete_history(self, container_name: str) -> bool:
        """Delete history for a container. Returns True if deleted."""
        path = self._history_dir / f"{container_name}.yaml"
        if path.exists():
            path.unlink()
            return True
        return False

    def list_histories(self) -> list[BackupHistory]:
        """List all backup histories."""
        if not self._history_dir.exists():
            return []

        histories = []
        for path in self._history_dir.glob("*.yaml"):
            try:
                data = self._read_yaml(path)
                if data:
                    histories.append(BackupHistory.model_validate(data))
            except Exception:
                continue
        return histories

    # -------------------------------------------------------------------------
    # Metadata operations
    # -------------------------------------------------------------------------

    def save_backup_metadata(self, metadata: BackupMetadata) -> None:
        """Save metadata for a backup."""
        container_dir = self._metadata_dir / metadata.container_name
        path = container_dir / f"{metadata.backup_id}.yaml"
        self._write_yaml(path, metadata.model_dump(mode="json"))

    def get_backup_metadata(
        self, container_name: str, backup_id: str
    ) -> BackupMetadata | None:
        """Get metadata for a specific backup."""
        path = self._metadata_dir / container_name / f"{backup_id}.yaml"
        data = self._read_yaml(path)
        if data is None:
            return None
        return BackupMetadata.model_validate(data)

    def get_backup_by_name(
        self, container_name: str, backup_name: str
    ) -> BackupMetadata | None:
        """Get metadata for a backup by name."""
        container_dir = self._metadata_dir / container_name
        if not container_dir.exists():
            return None

        for path in container_dir.glob("*.yaml"):
            try:
                data = self._read_yaml(path)
                if data and data.get("backup_name") == backup_name:
                    return BackupMetadata.model_validate(data)
            except Exception:
                continue
        return None

    def list_backups(self, container_name: str) -> list[BackupMetadata]:
        """List all backups for a container."""
        container_dir = self._metadata_dir / container_name
        if not container_dir.exists():
            return []

        backups = []
        for path in container_dir.glob("*.yaml"):
            try:
                data = self._read_yaml(path)
                if data:
                    backups.append(BackupMetadata.model_validate(data))
            except Exception:
                continue

        # Sort by creation date (newest first)
        backups.sort(key=lambda b: b.created_at, reverse=True)
        return backups

    def delete_backup_metadata(self, container_name: str, backup_id: str) -> bool:
        """Delete metadata for a backup. Returns True if deleted."""
        path = self._metadata_dir / container_name / f"{backup_id}.yaml"
        if path.exists():
            path.unlink()

            # Clean up empty container directory
            container_dir = path.parent
            if container_dir.exists() and not any(container_dir.iterdir()):
                container_dir.rmdir()

            return True
        return False

    def append_backup(self, metadata: BackupMetadata) -> None:
        """Add a backup to history and save metadata."""
        # Save metadata
        self.save_backup_metadata(metadata)

        # Update history
        history = self.get_history(metadata.container_name)
        history.add_backup(metadata)
        self.save_history(history)


# Global instance
_store: BackupStore | None = None


def get_backup_store() -> BackupStore:
    """Get the global backup store instance."""
    global _store
    if _store is None:
        _store = BackupStore()
    return _store
