"""High-level API for container backup management."""

from __future__ import annotations

import logging
from datetime import datetime
from typing import TYPE_CHECKING, Callable

from gaol.backup.exceptions import (
    BackupNotFoundError,
    ContainerNotFoundError,
    PolicyNotFoundError,
)
from gaol.backup.executor import BackupExecutor
from gaol.backup.models import (
    BackupHistory,
    BackupMetadata,
    BackupPolicy,
    BackupResult,
    BackupSchedule,
    BackupScheduleType,
    BackupType,
    GlobalBackupConfig,
    RestoreResult,
    RetentionPolicy,
    StorageConfig,
)
from gaol.backup.retention import RetentionEnforcer
from gaol.backup.storage import get_storage_backend
from gaol.backup.store import BackupStore, get_backup_store
from gaol.store import get_container_store

if TYPE_CHECKING:
    from gaol.backup.scheduler import BackupScheduler
    from gaol.backup.storage.base import StorageBackend
    from gaol.runtimes.base import ContainerRuntime
    from gaol.store import ContainerStore, StoredContainer


logger = logging.getLogger(__name__)


class BackupManager:
    """High-level API for container backup management.

    Provides a unified interface for:
    - Creating and restoring backups
    - Managing backup policies and schedules
    - Viewing backup history
    - Configuring storage backends

    Example:
        >>> manager = get_backup_manager()
        >>> result = manager.create("mycontainer", name="daily-backup")
        >>> manager.set_schedule("mycontainer", BackupScheduleType.DAILY, "02:00")
        >>> manager.restore("mycontainer", "daily-backup")
    """

    def __init__(
        self,
        container_store: ContainerStore | None = None,
        backup_store: BackupStore | None = None,
        runtime_factory: Callable[[StoredContainer], ContainerRuntime] | None = None,
    ) -> None:
        """Initialize the backup manager.

        Args:
            container_store: Container store (defaults to global instance)
            backup_store: Backup store (defaults to global instance)
            runtime_factory: Factory function to create runtime for a container
        """
        self._container_store = container_store or get_container_store()
        self._backup_store = backup_store or get_backup_store()
        self._runtime_factory = runtime_factory or self._default_runtime_factory
        self._scheduler: BackupScheduler | None = None

    def _default_runtime_factory(self, stored: StoredContainer) -> ContainerRuntime:
        """Create runtime for a container."""
        from gaol.runtimes import get_runtime

        return get_runtime(stored.config.runtime)

    def _get_container(self, name: str) -> StoredContainer:
        """Get a container by name or raise error."""
        stored = self._container_store.get(name)
        if stored is None:
            raise ContainerNotFoundError(name)
        return stored

    def _get_storage(self, container_name: str) -> StorageBackend:
        """Get storage backend for a container."""
        policy = self._backup_store.get_policy(container_name)
        if policy and policy.storage:
            return get_storage_backend(policy.storage)

        global_config = self._backup_store.get_global_config()
        return get_storage_backend(global_config.default_storage)

    # -------------------------------------------------------------------------
    # Backup Operations
    # -------------------------------------------------------------------------

    def create(
        self,
        container_name: str,
        name: str | None = None,
        backup_type: BackupType | None = None,
    ) -> BackupResult:
        """Create a backup of a container.

        Args:
            container_name: Name of the container
            name: Optional backup name (auto-generated if not provided)
            backup_type: Type of backup (defaults to policy or FULL)

        Returns:
            BackupResult with operation outcome
        """
        stored = self._get_container(container_name)
        runtime = self._runtime_factory(stored)
        storage = self._get_storage(container_name)

        # Get policy for configuration
        policy = self._backup_store.get_policy(container_name)
        btype = backup_type or (policy.backup_type if policy else BackupType.FULL)
        compression = policy.compression if policy else "gzip"

        # Generate backup name if not provided
        if name is None:
            timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
            name = f"{container_name}-{timestamp}"

        executor = BackupExecutor(runtime, storage)

        # Create backup based on type
        if btype == BackupType.CONFIG_ONLY:
            result, metadata = executor.create_config_backup(
                stored.config, container_name, name
            )
        else:
            # Full backup (incremental not yet implemented in manager)
            container_id = stored.runtime_id
            if container_id is None:
                # For containers that aren't running, we can still backup config
                result, metadata = executor.create_config_backup(
                    stored.config, container_name, name
                )
            else:
                pre_cmds = policy.pre_backup_commands if policy else None
                post_cmds = policy.post_backup_commands if policy else None

                result, metadata = executor.create_full_backup(
                    container_id,
                    container_name,
                    name,
                    compression=compression,
                    pre_commands=pre_cmds,
                    post_commands=post_cmds,
                )

        # Save metadata to store
        if metadata:
            self._backup_store.append_backup(metadata)

            # Apply retention policy
            if policy:
                enforcer = RetentionEnforcer(self._backup_store, storage)
                enforcer.apply_retention(container_name, policy.retention)

        return result

    def restore(
        self,
        container_name: str,
        backup_name: str,
        create_safety_snapshot: bool = True,
    ) -> RestoreResult:
        """Restore a container from a backup.

        Args:
            container_name: Name of the container
            backup_name: Name of the backup to restore
            create_safety_snapshot: Create snapshot before restore

        Returns:
            RestoreResult with operation outcome
        """
        stored = self._get_container(container_name)
        runtime = self._runtime_factory(stored)
        storage = self._get_storage(container_name)

        # Find backup metadata
        metadata = self._backup_store.get_backup_by_name(container_name, backup_name)
        if metadata is None:
            raise BackupNotFoundError(container_name, backup_name)

        executor = BackupExecutor(runtime, storage)
        return executor.restore_full_backup(
            stored, metadata, create_safety_snapshot=create_safety_snapshot
        )

    def list_backups(self, container_name: str) -> list[BackupMetadata]:
        """List all backups for a container.

        Args:
            container_name: Container name

        Returns:
            List of backup metadata, newest first
        """
        return self._backup_store.list_backups(container_name)

    def delete_backup(self, container_name: str, backup_name: str) -> bool:
        """Delete a specific backup.

        Args:
            container_name: Container name
            backup_name: Backup name to delete

        Returns:
            True if backup was deleted
        """
        metadata = self._backup_store.get_backup_by_name(container_name, backup_name)
        if metadata is None:
            return False

        storage = self._get_storage(container_name)

        # Delete from storage
        try:
            storage.delete(metadata.storage_path)
        except Exception as e:
            logger.warning(f"Failed to delete backup file: {e}")

        # Delete metadata
        self._backup_store.delete_backup_metadata(container_name, metadata.backup_id)

        # Update history
        history = self._backup_store.get_history(container_name)
        history.remove_backup(metadata.backup_id)
        self._backup_store.save_history(history)

        return True

    def verify(self, container_name: str, backup_name: str) -> bool:
        """Verify backup integrity.

        Args:
            container_name: Container name
            backup_name: Backup name to verify

        Returns:
            True if verification passed
        """
        stored = self._get_container(container_name)
        runtime = self._runtime_factory(stored)
        storage = self._get_storage(container_name)

        metadata = self._backup_store.get_backup_by_name(container_name, backup_name)
        if metadata is None:
            raise BackupNotFoundError(container_name, backup_name)

        executor = BackupExecutor(runtime, storage)
        return executor.verify_backup(metadata)

    # -------------------------------------------------------------------------
    # Policy Management
    # -------------------------------------------------------------------------

    def get_policy(self, container_name: str) -> BackupPolicy | None:
        """Get backup policy for a container."""
        return self._backup_store.get_policy(container_name)

    def set_policy(self, policy: BackupPolicy) -> None:
        """Set backup policy for a container."""
        self._get_container(policy.container_name)
        self._backup_store.save_policy(policy)

    def delete_policy(self, container_name: str) -> bool:
        """Delete backup policy for a container."""
        return self._backup_store.delete_policy(container_name)

    def set_schedule(
        self,
        container_name: str,
        schedule_type: BackupScheduleType,
        time_of_day: str = "02:00",
        day_of_week: int = 0,
        day_of_month: int = 1,
    ) -> BackupPolicy:
        """Set or update the backup schedule for a container.

        Args:
            container_name: Container name
            schedule_type: Schedule type (daily, weekly, monthly, manual)
            time_of_day: Time of day for backups (HH:MM)
            day_of_week: Day of week for weekly backups (0=Monday)
            day_of_month: Day of month for monthly backups

        Returns:
            Updated BackupPolicy
        """
        self._get_container(container_name)

        policy = self._backup_store.get_policy(container_name)

        schedule = BackupSchedule(
            schedule_type=schedule_type,
            time_of_day=time_of_day,
            day_of_week=day_of_week,
            day_of_month=day_of_month,
            enabled=schedule_type != BackupScheduleType.MANUAL,
        )

        if policy:
            policy = BackupPolicy(
                container_name=container_name,
                schedule=schedule,
                retention=policy.retention,
                storage=policy.storage,
                backup_type=policy.backup_type,
                prefer_incremental=policy.prefer_incremental,
                compression=policy.compression,
                pre_backup_commands=policy.pre_backup_commands,
                post_backup_commands=policy.post_backup_commands,
                verify_after_backup=policy.verify_after_backup,
                notify_on_failure=policy.notify_on_failure,
            )
        else:
            policy = BackupPolicy(container_name=container_name, schedule=schedule)

        self._backup_store.save_policy(policy)
        return policy

    def enable_schedule(self, container_name: str) -> None:
        """Enable scheduled backups for a container."""
        policy = self._backup_store.get_policy(container_name)
        if policy is None:
            raise PolicyNotFoundError(container_name)

        new_schedule = BackupSchedule(
            schedule_type=policy.schedule.schedule_type,
            time_of_day=policy.schedule.time_of_day,
            day_of_week=policy.schedule.day_of_week,
            day_of_month=policy.schedule.day_of_month,
            enabled=True,
        )

        new_policy = BackupPolicy(
            container_name=container_name,
            schedule=new_schedule,
            retention=policy.retention,
            storage=policy.storage,
            backup_type=policy.backup_type,
            prefer_incremental=policy.prefer_incremental,
            compression=policy.compression,
            pre_backup_commands=policy.pre_backup_commands,
            post_backup_commands=policy.post_backup_commands,
            verify_after_backup=policy.verify_after_backup,
            notify_on_failure=policy.notify_on_failure,
        )
        self._backup_store.save_policy(new_policy)

    def disable_schedule(self, container_name: str) -> None:
        """Disable scheduled backups for a container."""
        policy = self._backup_store.get_policy(container_name)
        if policy is None:
            raise PolicyNotFoundError(container_name)

        new_schedule = BackupSchedule(
            schedule_type=policy.schedule.schedule_type,
            time_of_day=policy.schedule.time_of_day,
            day_of_week=policy.schedule.day_of_week,
            day_of_month=policy.schedule.day_of_month,
            enabled=False,
        )

        new_policy = BackupPolicy(
            container_name=container_name,
            schedule=new_schedule,
            retention=policy.retention,
            storage=policy.storage,
            backup_type=policy.backup_type,
            prefer_incremental=policy.prefer_incremental,
            compression=policy.compression,
            pre_backup_commands=policy.pre_backup_commands,
            post_backup_commands=policy.post_backup_commands,
            verify_after_backup=policy.verify_after_backup,
            notify_on_failure=policy.notify_on_failure,
        )
        self._backup_store.save_policy(new_policy)

    # -------------------------------------------------------------------------
    # History
    # -------------------------------------------------------------------------

    def history(
        self, container_name: str, limit: int | None = None
    ) -> BackupHistory:
        """Get backup history for a container.

        Args:
            container_name: Container name
            limit: Maximum number of results

        Returns:
            BackupHistory with results
        """
        history = self._backup_store.get_history(container_name)
        if limit and len(history.backups) > limit:
            return BackupHistory(
                container_name=history.container_name,
                backups=history.backups[-limit:],
                last_backup=history.last_backup,
                last_successful_backup=history.last_successful_backup,
                total_backups=history.total_backups,
                successful_backups=history.successful_backups,
                failed_backups=history.failed_backups,
                total_size_bytes=history.total_size_bytes,
            )
        return history

    def clear_history(self, container_name: str) -> bool:
        """Clear backup history for a container."""
        return self._backup_store.delete_history(container_name)

    # -------------------------------------------------------------------------
    # Listing
    # -------------------------------------------------------------------------

    def list_policies(self) -> list[BackupPolicy]:
        """List all backup policies."""
        return self._backup_store.list_policies()

    def list_scheduled(self) -> list[tuple[str, BackupPolicy]]:
        """List all containers with scheduled backups."""
        policies = self._backup_store.list_policies()
        return [
            (p.container_name, p)
            for p in policies
            if p.schedule.enabled
            and p.schedule.schedule_type != BackupScheduleType.MANUAL
        ]

    # -------------------------------------------------------------------------
    # Retention
    # -------------------------------------------------------------------------

    def apply_retention(
        self, container_name: str, dry_run: bool = False
    ) -> list[BackupMetadata]:
        """Apply retention policy to a container's backups.

        Args:
            container_name: Container name
            dry_run: If True, don't actually delete anything

        Returns:
            List of backups deleted (or would be deleted)
        """
        policy = self._backup_store.get_policy(container_name)
        if policy is None:
            # Use default retention
            retention = RetentionPolicy()
        else:
            retention = policy.retention

        storage = self._get_storage(container_name)
        enforcer = RetentionEnforcer(self._backup_store, storage)
        return enforcer.apply_retention(container_name, retention, dry_run=dry_run)

    def apply_retention_all(
        self, dry_run: bool = False
    ) -> dict[str, list[BackupMetadata]]:
        """Apply retention policies to all containers."""
        storage = get_storage_backend()  # Use default storage
        enforcer = RetentionEnforcer(self._backup_store, storage)
        return enforcer.apply_retention_all(dry_run=dry_run)

    # -------------------------------------------------------------------------
    # Global Config
    # -------------------------------------------------------------------------

    def get_global_config(self) -> GlobalBackupConfig:
        """Get global backup configuration."""
        return self._backup_store.get_global_config()

    def set_storage(self, config: StorageConfig) -> None:
        """Set default storage configuration."""
        global_config = self._backup_store.get_global_config()
        global_config.default_storage = config
        self._backup_store.save_global_config(global_config)

    # -------------------------------------------------------------------------
    # Scheduler Management
    # -------------------------------------------------------------------------

    def start_scheduler(self) -> None:
        """Start the background backup scheduler."""
        if self._scheduler is None:
            from gaol.backup.scheduler import get_backup_scheduler

            self._scheduler = get_backup_scheduler(
                self._backup_store,
                on_backup=self.create,
            )
        self._scheduler.start()

    def stop_scheduler(self) -> None:
        """Stop the background backup scheduler."""
        if self._scheduler:
            self._scheduler.stop()

    def get_next_backup(self, container_name: str) -> datetime | None:
        """Get next scheduled backup time for a container."""
        if self._scheduler:
            return self._scheduler.get_next_backup(container_name)
        return None


# Global manager instance
_manager: BackupManager | None = None


def get_backup_manager() -> BackupManager:
    """Get the global backup manager instance."""
    global _manager
    if _manager is None:
        _manager = BackupManager()
    return _manager
