"""
DSS Project Models

Pydantic models for project configuration and state.
"""

from datetime import datetime
from enum import Enum
from pathlib import Path
from typing import Any, Dict, List, Optional, Union
from pydantic import BaseModel, Field, field_validator


class ProjectStatus(str, Enum):
    """Project lifecycle status."""
    CREATED = "created"
    CONFIGURED = "configured"
    SYNCED = "synced"
    BUILT = "built"
    ERROR = "error"


class FigmaFile(BaseModel):
    """A single Figma file reference."""
    key: str = Field(..., description="Figma file key from URL")
    name: str = Field(..., description="Human-readable file name")
    last_synced: Optional[datetime] = Field(None, description="Last sync timestamp")
    thumbnail_url: Optional[str] = Field(None, description="Figma thumbnail URL")

    class Config:
        json_encoders = {datetime: lambda v: v.isoformat() if v else None}


class FigmaSource(BaseModel):
    """Figma project source configuration.

    The team folder is the main Figma resource. Projects within the team
    contain design files. The 'uikit' file (if present) is the primary
    reference for design tokens.
    """
    team_id: Optional[str] = Field(None, description="Figma team ID (main resource)")
    project_id: Optional[str] = Field(None, description="Figma project ID within team")
    project_name: Optional[str] = Field(None, description="Figma project name")
    files: List[FigmaFile] = Field(default_factory=list, description="List of Figma files")
    uikit_file_key: Optional[str] = Field(None, description="Key of the UIKit reference file")
    auto_sync: bool = Field(False, description="Enable automatic sync on changes")

    def add_file(self, key: str, name: str, thumbnail_url: Optional[str] = None) -> FigmaFile:
        """Add a file to the source."""
        file = FigmaFile(key=key, name=name, thumbnail_url=thumbnail_url)
        # Check for duplicates
        if not any(f.key == key for f in self.files):
            self.files.append(file)
        return file

    def get_file(self, key: str) -> Optional[FigmaFile]:
        """Get a file by key."""
        for f in self.files:
            if f.key == key:
                return f
        return None


class OutputConfig(BaseModel):
    """Output configuration for generated files."""
    tokens_dir: str = Field("./tokens", description="Directory for token files")
    themes_dir: str = Field("./themes", description="Directory for theme files")
    components_dir: str = Field("./components", description="Directory for component files")
    formats: List[str] = Field(
        default_factory=lambda: ["css", "scss", "json"],
        description="Output formats to generate"
    )

    @field_validator("formats")
    @classmethod
    def validate_formats(cls, v):
        valid = {"css", "scss", "json", "js", "ts"}
        for fmt in v:
            if fmt not in valid:
                raise ValueError(f"Invalid format: {fmt}. Must be one of {valid}")
        return v


class ProjectConfig(BaseModel):
    """Main project configuration (ds.config.json)."""
    name: str = Field(..., description="Project name")
    version: str = Field("1.0.0", description="Project version")
    description: Optional[str] = Field(None, description="Project description")

    # Sources
    figma: Optional[FigmaSource] = Field(None, description="Figma source configuration")

    # Design system settings
    skin: Optional[str] = Field(None, description="Base skin/theme to extend (e.g., 'shadcn', 'material')")
    base_theme: str = Field("light", description="Default theme variant")

    # Output configuration
    output: OutputConfig = Field(default_factory=OutputConfig, description="Output settings")

    # Metadata
    created_at: datetime = Field(default_factory=datetime.now)
    updated_at: datetime = Field(default_factory=datetime.now)

    class Config:
        json_encoders = {datetime: lambda v: v.isoformat() if v else None}


class DSSProject(BaseModel):
    """
    Complete DSS Project representation.

    Combines configuration with runtime state.
    """
    config: ProjectConfig = Field(..., description="Project configuration")
    path: Path = Field(..., description="Absolute path to project directory")
    status: ProjectStatus = Field(ProjectStatus.CREATED, description="Current project status")

    # Runtime state
    errors: List[str] = Field(default_factory=list, description="Error messages")
    warnings: List[str] = Field(default_factory=list, description="Warning messages")

    # Extracted data (populated after sync)
    extracted_tokens: Optional[Dict[str, Any]] = Field(None, description="Tokens from sources")

    class Config:
        arbitrary_types_allowed = True
        json_encoders = {
            datetime: lambda v: v.isoformat() if v else None,
            Path: str,
        }

    @property
    def config_path(self) -> Path:
        """Path to ds.config.json."""
        return self.path / "ds.config.json"

    @property
    def tokens_path(self) -> Path:
        """Path to tokens directory."""
        return self.path / self.config.output.tokens_dir

    @property
    def themes_path(self) -> Path:
        """Path to themes directory."""
        return self.path / self.config.output.themes_dir

    def to_config_dict(self) -> Dict[str, Any]:
        """Export configuration for saving to ds.config.json."""
        return self.config.model_dump(mode="json", exclude_none=True)

    @classmethod
    def from_config_file(cls, config_path: Path) -> "DSSProject":
        """Load project from ds.config.json file."""
        import json

        if not config_path.exists():
            raise FileNotFoundError(f"Config file not found: {config_path}")

        with open(config_path, "r") as f:
            config_data = json.load(f)

        config = ProjectConfig(**config_data)
        project_path = config_path.parent

        return cls(
            config=config,
            path=project_path,
            status=ProjectStatus.CONFIGURED,
        )
