"""
Multi-tier storage configuration schemas from assets.yaml.

These schemas define the storage tier architecture, sync configuration,
and project auto-load rules for media asset management.

SST File: lightwave/schema/definitions/data/assets/assets.yaml
Database Model: apps.platform.media.models.MediaAsset, AssetLocation, SyncJob

Architecture:
    Tier 0 (Hot)  - Local SSD     - Active project files (~1ms)
    Tier 1 (Warm) - NAS (SMB)     - Recent projects, large files (~10ms)
    Tier 2 (Cold) - CDN/S3        - Archive + public delivery (~100ms)

Usage:
    from lightwave.schema.pydantic.sst import (
        StorageTierConfig,
        SyncConfig,
        ProjectAutoLoadConfig,
        get_storage_config,
    )

    # Load storage configuration
    config = get_storage_config()
    print(config.tiers["local_ssd"].latency_target_ms)  # 1
    print(config.sync.max_concurrent_downloads)  # 4
    print(config.project_auto_load.priority_weights["project_file"])  # 100
"""

from __future__ import annotations

from enum import Enum
from functools import lru_cache
from typing import Literal

import yaml
from pydantic import Field, field_validator

from lightwave.schema.pydantic.core.base import SSTSchema

# =============================================================================
# ENUMS
# =============================================================================


class StorageTierName(str, Enum):
    """Storage tier identifiers."""

    LOCAL_SSD = "local_ssd"
    NAS = "nas"
    CDN_S3 = "cdn_s3"


class SyncState(str, Enum):
    """Asset sync state across storage tiers."""

    SYNCED = "synced"
    PENDING_UPLOAD = "pending_upload"
    PENDING_DOWNLOAD = "pending_download"
    UPLOADING = "uploading"
    DOWNLOADING = "downloading"
    FAILED = "failed"
    MISSING = "missing"
    STALE = "stale"


class ConflictStrategy(str, Enum):
    """Sync conflict resolution strategies."""

    SERVER_WINS = "server_wins"
    LOCAL_WINS = "local_wins"
    NEWEST_WINS = "newest_wins"
    MANUAL = "manual"
    KEEP_BOTH = "keep_both"


class SyncDirection(str, Enum):
    """Direction of sync operation."""

    UPLOAD = "upload"
    DOWNLOAD = "download"
    BIDIRECTIONAL = "bidirectional"


class AssetLocationType(str, Enum):
    """Status of asset at a specific storage location."""

    PRESENT = "present"
    ABSENT = "absent"
    PENDING = "pending"
    CORRUPTED = "corrupted"
    ARCHIVED = "archived"


class TransferPriority(str, Enum):
    """Priority levels for sync operations."""

    CRITICAL = "critical"
    HIGH = "high"
    NORMAL = "normal"
    LOW = "low"
    BACKGROUND = "background"


class MediaAssetType(str, Enum):
    """Types of media assets in the system."""

    # Video
    VIDEO_RAW = "video_raw"
    VIDEO_PROXY = "video_proxy"
    VIDEO_DELIVERABLE = "video_deliverable"
    # Audio
    AUDIO_RAW = "audio_raw"
    AUDIO_MIX = "audio_mix"
    # Images
    IMAGE_RAW = "image_raw"
    IMAGE_PROCESSED = "image_processed"
    # Project files
    PROJECT_FILE = "project_file"
    # Other
    GENERIC = "generic"


class MediaProjectState(str, Enum):
    """Media project lifecycle states."""

    DRAFT = "draft"
    ACTIVE = "active"
    REVIEW = "review"
    DELIVERED = "delivered"
    ARCHIVED = "archived"


class SyncJobState(str, Enum):
    """Sync job execution states."""

    PENDING = "pending"
    QUEUED = "queued"
    IN_PROGRESS = "in_progress"
    COMPLETED = "completed"
    FAILED = "failed"
    CANCELLED = "cancelled"
    PAUSED = "paused"


# =============================================================================
# CDN MULTI-LAYER CONFIGURATION
# =============================================================================


class CDNDNSConfig(SSTSchema):
    """DNS layer configuration (Cloudflare)."""

    provider: Literal["cloudflare", "route53", "custom"] = Field("cloudflare", description="DNS provider")
    zone_id: str = Field("${CLOUDFLARE_ZONE_ID}", description="Cloudflare Zone ID")
    api_token: str = Field("${CLOUDFLARE_API_TOKEN}", description="Cloudflare API token")


class CDNEdgeConfig(SSTSchema):
    """Edge cache layer configuration (Cloudflare)."""

    provider: Literal["cloudflare", "cloudfront", "fastly"] = Field("cloudflare", description="Edge cache provider")
    purge_endpoint: str = Field(
        "https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache",
        description="Cache purge API endpoint",
    )
    default_edge_ttl: int = Field(604800, ge=0, description="Default TTL at edge in seconds (7 days)")
    default_browser_ttl: int = Field(2592000, ge=0, description="Default browser TTL in seconds (30 days)")


class CDNOriginConfig(SSTSchema):
    """Origin layer configuration (AWS S3)."""

    provider: Literal["aws_s3", "gcs", "azure_blob"] = Field("aws_s3", description="Origin storage provider")
    bucket: str = Field(..., description="S3 bucket name")
    region: str = Field("us-east-1", description="AWS region")
    endpoint: str | None = Field(None, description="S3 website endpoint (for Cloudflare proxy)")


class CDNMultiLayerConfig(SSTSchema):
    """
    Multi-layer CDN configuration.

    Architecture: User → Cloudflare (DNS + Edge) → S3 (Origin)
    """

    base_url: str = Field(..., description="Public-facing CDN URL")
    dns: CDNDNSConfig = Field(default_factory=CDNDNSConfig, description="DNS layer")
    edge: CDNEdgeConfig = Field(default_factory=CDNEdgeConfig, description="Edge cache layer")
    origin: CDNOriginConfig | None = Field(None, description="Origin storage layer")

    # Path prefixes (inherited from existing config)
    paths: dict[str, str] = Field(
        default_factory=lambda: {
            "images": "/images/",
            "static": "/static/",
            "media": "/media/",
            "uploads": "/uploads/",
            "fonts": "/fonts/",
            "scripts": "/js/",
            "styles": "/css/",
        },
        description="Asset path prefixes",
    )


# =============================================================================
# STORAGE TIER CONFIGURATION
# =============================================================================


class StorageTierConfig(SSTSchema):
    """
    Configuration for a single storage tier.

    Tier Level Mapping:
        0 = Hot  (Local SSD) - fastest, smallest, auto-evicts to tier 1
        1 = Warm (NAS)       - fast local, larger, auto-evicts to tier 2
        2 = Cold (S3/CDN)    - slowest, unlimited, archive + delivery
    """

    name: str = Field(..., description="Human-readable tier name")
    tier_level: int = Field(..., ge=0, le=2, description="Tier level (0=hot, 1=warm, 2=cold)")
    latency_target_ms: int = Field(..., ge=1, description="Target latency in milliseconds")
    is_local: bool = Field(..., description="Whether this is local storage")
    priority: int = Field(..., ge=1, description="Sync priority (lower = higher)")
    description: str = Field("", description="Tier description")

    # Protocol settings (for NAS tier)
    protocol: Literal["smb", "nfs", "local", "s3"] | None = Field(None, description="Storage protocol")
    discovery: Literal["bonjour", "manual", "auto"] | None = Field(None, description="Network discovery method")
    mount_point: str | None = Field(None, description="Local mount point path")
    share_name: str | None = Field(None, description="Network share name")

    # Cloud settings (for S3 tier)
    provider: Literal["aws_s3", "gcs", "azure_blob"] | None = Field(None, description="Cloud storage provider")
    storage_class: str | None = Field(None, description="S3 storage class")
    archive_storage_class: str | None = Field(None, description="S3 archive storage class (e.g., GLACIER_IR)")

    # Lifecycle settings
    auto_evict_days: int | None = Field(None, ge=1, description="Auto-evict to next tier after N days inactive")
    archive_after_days: int | None = Field(None, ge=1, description="Archive to cold storage after N days")

    @field_validator("tier_level")
    @classmethod
    def validate_tier_level(cls, v: int) -> int:
        """Validate tier level is within expected range."""
        if v not in [0, 1, 2]:
            msg = f"tier_level must be 0, 1, or 2, got {v}"
            raise ValueError(msg)
        return v


# =============================================================================
# SYNC CONFIGURATION
# =============================================================================


class SyncConfig(SSTSchema):
    """
    Sync operation configuration for multi-tier storage.

    Controls concurrency, chunking, retries, and bandwidth limits.
    """

    # Concurrent operations
    max_concurrent_downloads: int = Field(4, ge=1, le=16, description="Max parallel downloads")
    max_concurrent_uploads: int = Field(2, ge=1, le=8, description="Max parallel uploads")

    # Chunked transfer settings
    chunk_size_mb: int = Field(4, ge=1, le=100, description="Chunk size in MB")
    multipart_threshold_mb: int = Field(100, ge=10, description="Use multipart for files larger than this")

    # Retry settings
    retry_max: int = Field(3, ge=1, le=10, description="Max retry attempts")
    retry_backoff_base_seconds: int = Field(5, ge=1, description="Base retry backoff in seconds")

    # Conflict resolution
    default_conflict_strategy: ConflictStrategy = Field(
        ConflictStrategy.NEWEST_WINS, description="Default conflict resolution strategy"
    )

    # Bandwidth limits (0 = unlimited)
    upload_bandwidth_limit_mbps: int = Field(0, ge=0, description="Upload bandwidth limit in Mbps (0=unlimited)")
    download_bandwidth_limit_mbps: int = Field(0, ge=0, description="Download bandwidth limit in Mbps (0=unlimited)")

    # Background sync
    background_sync_enabled: bool = Field(True, description="Enable background sync daemon")
    background_sync_interval_minutes: int = Field(15, ge=1, description="Background sync interval in minutes")


# =============================================================================
# PROJECT AUTO-LOAD CONFIGURATION
# =============================================================================


class ProjectAutoLoadConfig(SSTSchema):
    """
    Configuration for automatic file syncing when a project is activated.

    Uses priority weights and file patterns to determine sync order.
    """

    # Priority scoring (higher = sync first)
    priority_weights: dict[str, int] = Field(
        default_factory=lambda: {
            "project_file": 100,
            "proxy": 90,
            "audio": 80,
            "image": 70,
            "raw_video": 50,
            "other": 30,
        },
        description="Priority weights by file category",
    )

    # File type patterns
    patterns: dict[str, list[str]] = Field(
        default_factory=lambda: {
            "project_file": [
                "*.fcpbundle",
                "*.fcpxml",
                "*.prproj",
                "*.aep",
                "*.drp",
                "*.blend",
            ],
            "proxy": ["*_proxy.*", "*/Proxies/*", "*/proxy/*"],
            "audio": ["*.wav", "*.aiff", "*.mp3", "*.aac"],
            "image": ["*.jpg", "*.jpeg", "*.png", "*.tiff", "*.exr", "*.dpx"],
            "raw_video": ["*.mov", "*.mp4", "*.mxf", "*.r3d", "*.braw", "*.ari"],
        },
        description="File patterns by category",
    )

    # Size limits
    max_auto_load_gb: int = Field(50, ge=1, description="Max auto-load size per project in GB")
    skip_files_larger_than_gb: int = Field(10, ge=1, description="Skip files larger than this (require manual request)")


# =============================================================================
# ROOT STORAGE CONFIGURATION
# =============================================================================


class StorageConfigSchema(SSTSchema):
    """
    Complete multi-tier storage configuration.

    Loaded from the storage_tiers, sync_config, and project_auto_load
    sections of assets.yaml.
    """

    tiers: dict[str, StorageTierConfig] = Field(..., description="Storage tier configurations")
    sync: SyncConfig = Field(default_factory=SyncConfig, description="Sync operation settings")
    project_auto_load: ProjectAutoLoadConfig = Field(
        default_factory=ProjectAutoLoadConfig, description="Auto-load configuration"
    )
    cdn: CDNMultiLayerConfig | None = Field(None, description="CDN configuration")

    def get_tier(self, tier_name: str | StorageTierName) -> StorageTierConfig | None:
        """Get a specific storage tier configuration."""
        key = tier_name.value if isinstance(tier_name, StorageTierName) else tier_name
        return self.tiers.get(key)

    def get_tier_by_level(self, level: int) -> StorageTierConfig | None:
        """Get storage tier by tier level (0, 1, or 2)."""
        for tier in self.tiers.values():
            if tier.tier_level == level:
                return tier
        return None

    def get_tiers_sorted_by_priority(self) -> list[StorageTierConfig]:
        """Get all tiers sorted by priority (lowest first = highest priority)."""
        return sorted(self.tiers.values(), key=lambda t: t.priority)

    def get_local_tiers(self) -> list[StorageTierConfig]:
        """Get all local storage tiers (SSD, NAS)."""
        return [t for t in self.tiers.values() if t.is_local]

    def get_remote_tiers(self) -> list[StorageTierConfig]:
        """Get all remote storage tiers (S3/CDN)."""
        return [t for t in self.tiers.values() if not t.is_local]


# =============================================================================
# LOADER FUNCTIONS
# =============================================================================


def _get_assets_yaml_path():
    """Get path to assets.yaml via SST registry."""
    from lightwave.schema.core.paths import get_sst_path

    return get_sst_path("assets")


@lru_cache(maxsize=1)
def get_storage_config() -> StorageConfigSchema:
    """
    Load multi-tier storage configuration from assets.yaml.

    Returns:
        StorageConfigSchema: Complete storage tier and sync configuration

    Raises:
        FileNotFoundError: If assets.yaml doesn't exist
        ValidationError: If YAML doesn't match schema

    Example:
        config = get_storage_config()
        print(config.tiers["local_ssd"].latency_target_ms)  # 1
        print(config.sync.max_concurrent_downloads)  # 4
    """
    yaml_path = _get_assets_yaml_path()

    if not yaml_path.exists():
        msg = f"Assets SST file not found: {yaml_path}"
        raise FileNotFoundError(msg)

    with yaml_path.open() as f:
        raw_data = yaml.safe_load(f)

    # Extract storage-related sections
    storage_tiers = raw_data.get("storage_tiers", {})
    sync_config = raw_data.get("sync_config", {})
    project_auto_load = raw_data.get("project_auto_load", {})
    cdn_data = raw_data.get("cdn", {})

    # Parse storage tiers
    tiers = {}
    for tier_name, tier_data in storage_tiers.items():
        tiers[tier_name] = StorageTierConfig(**tier_data)

    # Parse sync config
    sync = SyncConfig(**sync_config) if sync_config else SyncConfig()

    # Parse project auto-load
    auto_load = ProjectAutoLoadConfig(**project_auto_load) if project_auto_load else ProjectAutoLoadConfig()

    # Parse CDN config if present
    cdn = None
    if cdn_data:
        cdn = CDNMultiLayerConfig(
            base_url=cdn_data.get("base_url", ""),
            dns=CDNDNSConfig(**cdn_data.get("dns", {})) if "dns" in cdn_data else CDNDNSConfig(),
            edge=CDNEdgeConfig(**cdn_data.get("edge", {})) if "edge" in cdn_data else CDNEdgeConfig(),
            origin=CDNOriginConfig(**cdn_data["origin"]) if "origin" in cdn_data else None,
            paths=cdn_data.get("paths", {}),
        )

    return StorageConfigSchema(
        tiers=tiers,
        sync=sync,
        project_auto_load=auto_load,
        cdn=cdn,
    )


def get_tier_for_sync_state(state: SyncState) -> StorageTierName | None:
    """
    Determine which tier should handle a given sync state.

    Args:
        state: Current sync state

    Returns:
        Target tier name or None if not applicable
    """
    upload_states = {SyncState.PENDING_UPLOAD, SyncState.UPLOADING}
    download_states = {SyncState.PENDING_DOWNLOAD, SyncState.DOWNLOADING}

    if state in upload_states:
        return StorageTierName.CDN_S3  # Uploads go to cloud
    if state in download_states:
        return StorageTierName.LOCAL_SSD  # Downloads go to local
    return None


def clear_storage_config_cache() -> None:
    """Clear cached storage configuration (useful for testing)."""
    get_storage_config.cache_clear()


# =============================================================================
# EXPORTS
# =============================================================================

__all__ = [
    # Enums
    "StorageTierName",
    "SyncState",
    "ConflictStrategy",
    "SyncDirection",
    "AssetLocationType",
    "TransferPriority",
    "MediaAssetType",
    "MediaProjectState",
    "SyncJobState",
    # CDN Configuration
    "CDNDNSConfig",
    "CDNEdgeConfig",
    "CDNOriginConfig",
    "CDNMultiLayerConfig",
    # Storage Configuration
    "StorageTierConfig",
    "SyncConfig",
    "ProjectAutoLoadConfig",
    "StorageConfigSchema",
    # Loader functions
    "get_storage_config",
    "get_tier_for_sync_state",
    "clear_storage_config_cache",
]
