from pydantic import BaseModel, Field, validator
from typing import Optional, List, Dict
from datetime import date, datetime
from enum import Enum
import re

from models.project import ProjectStatus, ProjectType
from models.user import DepartmentRole


# Technical Specifications Schemas
class DeliveryMovieSpec(BaseModel):
    """Delivery movie specifications for a department"""
    resolution: str = Field(..., description="Movie resolution (e.g., '1920x1080', '2048x1080')")
    format: str = Field(..., description="Movie format (e.g., 'mov', 'mp4', 'exr')")
    codec: Optional[str] = Field(None, description="Video codec (e.g., 'h264', 'prores', 'dnxhd')")
    quality: Optional[str] = Field(None, description="Quality setting (e.g., 'high', 'medium', 'low')")

    @validator('resolution')
    def validate_resolution(cls, v):
        if not re.match(r'^\d+x\d+$', v):
            raise ValueError('Resolution must be in format "WIDTHxHEIGHT" (e.g., "1920x1080")')
        return v

    @validator('format')
    def validate_format(cls, v):
        allowed_formats = ['mov', 'mp4', 'exr', 'dpx', 'tiff']
        if v.lower() not in allowed_formats:
            raise ValueError(f'Format must be one of: {", ".join(allowed_formats)}')
        return v.lower()

    @validator('codec')
    def validate_codec(cls, v):
        if v is None:
            return v
        allowed_codecs = ['h264', 'h265', 'prores', 'dnxhd', 'dnxhr', 'uncompressed']
        if v.lower() not in allowed_codecs:
            raise ValueError(f'Codec must be one of: {", ".join(allowed_codecs)}')
        return v.lower()

    @validator('quality')
    def validate_quality(cls, v):
        if v is None:
            return v
        allowed_qualities = ['low', 'medium', 'high', 'lossless']
        if v.lower() not in allowed_qualities:
            raise ValueError(f'Quality must be one of: {", ".join(allowed_qualities)}')
        return v.lower()


class ProjectTechnicalSpecs(BaseModel):
    """Technical specifications for a project"""
    frame_rate: Optional[float] = Field(None, ge=1.0, le=120.0, description="Frames per second (1-120 fps)")
    data_drive_path: Optional[str] = Field(None, description="Physical path for project data storage")
    publish_storage_path: Optional[str] = Field(None, description="Path for approved work delivery")
    delivery_image_resolution: Optional[str] = Field(None, description="Required image resolution (e.g., '1920x1080', '4096x2160')")
    delivery_movie_specs_by_department: Optional[Dict[str, DeliveryMovieSpec]] = Field(
        default_factory=dict,
        description="Delivery movie resolution and format specifications per department"
    )

    @validator('delivery_image_resolution')
    def validate_delivery_image_resolution(cls, v):
        if v is None:
            return v
        if not re.match(r'^\d+x\d+$', v):
            raise ValueError('Delivery image resolution must be in format "WIDTHxHEIGHT" (e.g., "1920x1080")')
        return v

    @validator('delivery_movie_specs_by_department')
    def validate_delivery_movie_specs_by_department(cls, v):
        if v is None:
            return {}
        allowed_departments = ['layout', 'animation', 'lighting', 'composite', 'modeling', 'rigging', 'surfacing']
        for dept in v.keys():
            if dept not in allowed_departments:
                raise ValueError(f'Department must be one of: {", ".join(allowed_departments)}')
        return v


# Default delivery movie specifications per department
DEFAULT_DELIVERY_MOVIE_SPECS = {
    "layout": DeliveryMovieSpec(
        resolution="1920x1080",
        format="mov",
        codec="h264",
        quality="medium"
    ),
    "animation": DeliveryMovieSpec(
        resolution="1920x1080", 
        format="mov",
        codec="h264",
        quality="high"
    ),
    "lighting": DeliveryMovieSpec(
        resolution="2048x1080",
        format="exr",
        codec=None,
        quality="high"
    ),
    "composite": DeliveryMovieSpec(
        resolution="2048x1080",
        format="mov",
        codec="prores",
        quality="high"
    )
}


class ProjectBase(BaseModel):
    name: str = Field(..., min_length=1, max_length=255)
    code_name: str = Field(..., min_length=1, max_length=50, description="Unique project code identifier")
    client_name: str = Field(..., min_length=1, max_length=255, description="Client or studio name")
    project_type: ProjectType = Field(..., description="Project type: TV, Cinema, or Game")
    description: Optional[str] = None
    status: ProjectStatus = ProjectStatus.PLANNING
    start_date: Optional[date] = None
    end_date: Optional[date] = None
    
    # Technical specifications
    frame_rate: Optional[float] = Field(None, ge=1.0, le=120.0, description="Frames per second (1-120 fps)")
    data_drive_path: Optional[str] = Field(None, description="Physical path for project data storage")
    publish_storage_path: Optional[str] = Field(None, description="Path for approved work delivery")
    delivery_image_resolution: Optional[str] = Field(None, description="Required image resolution")
    delivery_movie_specs_by_department: Optional[Dict[str, DeliveryMovieSpec]] = Field(
        default_factory=dict,
        description="Delivery movie specifications per department"
    )

    @validator('delivery_image_resolution')
    def validate_delivery_image_resolution(cls, v):
        if v is None:
            return v
        if not re.match(r'^\d+x\d+$', v):
            raise ValueError('Delivery image resolution must be in format "WIDTHxHEIGHT" (e.g., "1920x1080")')
        return v

    @validator('delivery_movie_specs_by_department')
    def validate_delivery_movie_specs_by_department(cls, v):
        if v is None:
            return {}
        allowed_departments = ['layout', 'animation', 'lighting', 'composite', 'modeling', 'rigging', 'surfacing']
        for dept in v.keys():
            if dept not in allowed_departments:
                raise ValueError(f'Department must be one of: {", ".join(allowed_departments)}')
        return v


class ProjectCreate(ProjectBase):
    pass


class ProjectUpdate(BaseModel):
    name: Optional[str] = Field(None, min_length=1, max_length=255)
    code_name: Optional[str] = Field(None, min_length=1, max_length=50, description="Unique project code identifier")
    client_name: Optional[str] = Field(None, min_length=1, max_length=255, description="Client or studio name")
    project_type: Optional[ProjectType] = Field(None, description="Project type: TV, Cinema, or Game")
    description: Optional[str] = None
    status: Optional[ProjectStatus] = None
    start_date: Optional[date] = None
    end_date: Optional[date] = None
    
    # Technical specifications
    frame_rate: Optional[float] = Field(None, ge=1.0, le=120.0, description="Frames per second (1-120 fps)")
    data_drive_path: Optional[str] = Field(None, description="Physical path for project data storage")
    publish_storage_path: Optional[str] = Field(None, description="Path for approved work delivery")
    delivery_image_resolution: Optional[str] = Field(None, description="Required image resolution")
    delivery_movie_specs_by_department: Optional[Dict[str, DeliveryMovieSpec]] = Field(
        None,
        description="Delivery movie specifications per department"
    )

    @validator('delivery_image_resolution')
    def validate_delivery_image_resolution(cls, v):
        if v is None:
            return v
        if not re.match(r'^\d+x\d+$', v):
            raise ValueError('Delivery image resolution must be in format "WIDTHxHEIGHT" (e.g., "1920x1080")')
        return v

    @validator('delivery_movie_specs_by_department')
    def validate_delivery_movie_specs_by_department(cls, v):
        if v is None:
            return v
        allowed_departments = ['layout', 'animation', 'lighting', 'composite', 'modeling', 'rigging', 'surfacing']
        for dept in v.keys():
            if dept not in allowed_departments:
                raise ValueError(f'Department must be one of: {", ".join(allowed_departments)}')
        return v


class ProjectMemberBase(BaseModel):
    user_id: int
    department_role: Optional[DepartmentRole] = None


class ProjectMemberCreate(ProjectMemberBase):
    pass


class ProjectMemberUpdate(BaseModel):
    department_role: Optional[DepartmentRole] = None


class ProjectMemberResponse(ProjectMemberBase):
    id: int
    project_id: int
    joined_at: datetime
    
    # User information
    user_email: str
    user_first_name: str
    user_last_name: str
    user_avatar_url: Optional[str] = None

    class Config:
        from_attributes = True


class ProjectResponse(ProjectBase):
    id: int
    created_at: datetime
    updated_at: datetime
    thumbnail_url: Optional[str] = None
    
    # Optional member list
    project_members: Optional[List[ProjectMemberResponse]] = None

    class Config:
        from_attributes = True


class ProjectListResponse(BaseModel):
    id: int
    name: str
    code_name: str
    client_name: str
    project_type: ProjectType
    description: Optional[str] = None
    status: ProjectStatus
    start_date: Optional[date] = None
    end_date: Optional[date] = None
    created_at: datetime
    updated_at: datetime
    thumbnail_url: Optional[str] = None
    
    # Summary information
    member_count: int = 0
    episode_count: int = 0
    asset_count: int = 0

    class Config:
        from_attributes = True


# Default asset tasks by category
DEFAULT_ASSET_TASKS = {
    "characters": ["modeling", "surfacing", "rigging"],
    "props": ["modeling", "surfacing"],
    "sets": ["modeling", "surfacing"],
    "vehicles": ["modeling", "surfacing", "rigging"]
}

# Default shot tasks
DEFAULT_SHOT_TASKS = ["layout", "animation", "simulation", "lighting", "compositing"]


class ProjectSettings(BaseModel):
    """Project-specific settings for upload location and task templates"""
    upload_data_location: Optional[str] = Field(None, description="Custom upload storage path for project files")
    asset_task_templates: Optional[Dict[str, List[str]]] = Field(
        default_factory=lambda: DEFAULT_ASSET_TASKS.copy(),
        description="Custom default tasks per asset category"
    )
    shot_task_templates: Optional[List[str]] = Field(
        default_factory=lambda: DEFAULT_SHOT_TASKS.copy(),
        description="Custom default tasks for shots"
    )
    enabled_asset_tasks: Optional[Dict[str, List[str]]] = Field(
        default_factory=dict,
        description="Enabled/disabled status for asset tasks per category"
    )
    enabled_shot_tasks: Optional[List[str]] = Field(
        default_factory=lambda: DEFAULT_SHOT_TASKS.copy(),
        description="Enabled/disabled shot tasks"
    )

    @validator('asset_task_templates')
    def validate_asset_task_templates(cls, v):
        if v is None:
            return DEFAULT_ASSET_TASKS.copy()
        allowed_categories = ['characters', 'props', 'sets', 'vehicles']
        for category in v.keys():
            if category not in allowed_categories:
                raise ValueError(f'Asset category must be one of: {", ".join(allowed_categories)}')
            if not isinstance(v[category], list):
                raise ValueError(f'Task templates for {category} must be a list')
        return v

    @validator('shot_task_templates')
    def validate_shot_task_templates(cls, v):
        if v is None:
            return DEFAULT_SHOT_TASKS.copy()
        if not isinstance(v, list):
            raise ValueError('Shot task templates must be a list')
        return v

    @validator('enabled_asset_tasks')
    def validate_enabled_asset_tasks(cls, v):
        if v is None:
            return {}
        allowed_categories = ['characters', 'props', 'sets', 'vehicles']
        for category in v.keys():
            if category not in allowed_categories:
                raise ValueError(f'Asset category must be one of: {", ".join(allowed_categories)}')
            if not isinstance(v[category], list):
                raise ValueError(f'Enabled tasks for {category} must be a list')
        return v

    @validator('enabled_shot_tasks')
    def validate_enabled_shot_tasks(cls, v):
        if v is None:
            return DEFAULT_SHOT_TASKS.copy()
        if not isinstance(v, list):
            raise ValueError('Enabled shot tasks must be a list')
        return v


class ProjectSettingsUpdate(BaseModel):
    """Update project settings"""
    upload_data_location: Optional[str] = Field(None, description="Custom upload storage path for project files")
    asset_task_templates: Optional[Dict[str, List[str]]] = Field(None, description="Custom default tasks per asset category")
    shot_task_templates: Optional[List[str]] = Field(None, description="Custom default tasks for shots")
    enabled_asset_tasks: Optional[Dict[str, List[str]]] = Field(None, description="Enabled/disabled asset tasks per category")
    enabled_shot_tasks: Optional[List[str]] = Field(None, description="Enabled/disabled shot tasks")

    @validator('asset_task_templates')
    def validate_asset_task_templates(cls, v):
        if v is None:
            return v
        allowed_categories = ['characters', 'props', 'sets', 'vehicles']
        for category in v.keys():
            if category not in allowed_categories:
                raise ValueError(f'Asset category must be one of: {", ".join(allowed_categories)}')
            if not isinstance(v[category], list):
                raise ValueError(f'Task templates for {category} must be a list')
        return v

    @validator('shot_task_templates')
    def validate_shot_task_templates(cls, v):
        if v is None:
            return v
        if not isinstance(v, list):
            raise ValueError('Shot task templates must be a list')
        return v

    @validator('enabled_asset_tasks')
    def validate_enabled_asset_tasks(cls, v):
        if v is None:
            return v
        allowed_categories = ['characters', 'props', 'sets', 'vehicles']
        for category in v.keys():
            if category not in allowed_categories:
                raise ValueError(f'Asset category must be one of: {", ".join(allowed_categories)}')
            if not isinstance(v[category], list):
                raise ValueError(f'Enabled tasks for {category} must be a list')
        return v

    @validator('enabled_shot_tasks')
    def validate_enabled_shot_tasks(cls, v):
        if v is None:
            return v
        if not isinstance(v, list):
            raise ValueError('Enabled shot tasks must be a list')
        return v