"""
Pydantic schemas for per-task-type submission configuration
"""
from pydantic import BaseModel, Field, validator
from typing import Optional, Dict, List
import re

from utils.file_handler import file_handler

# Tokens supported in a token-mode naming_pattern
NAMING_PATTERN_TOKEN_RE = re.compile(r'\{(name|task_type|task_name|project_name|project_code|version)\}')
NAMING_PATTERN_ALLOWED_CHARS_RE = re.compile(r'^[A-Za-z0-9_\-\.]*$')


MOVIE_FORMATS = {ext.lstrip('.') for ext in file_handler.MOVIE_EXTENSIONS}
MOVIE_CODECS = {'h264', 'h265', 'mjpeg', 'dnxhd', 'dnxhr', 'prores', 'uncompressed', 'avid', 'cineform'}
MOVIE_FRAME_RATES = {23.976, 24, 30, 48, 60}
RESOLUTION_RE = re.compile(r'^\d+x\d+$')


class SubmissionTypeConfig(BaseModel):
    """Submission rules for a single task type.

    All checking against these rules happens client-side (in TaskSubmissions.vue) when an
    artist picks a file to submit - this schema only validates and stores the configuration
    itself, it is never used to inspect an uploaded file server-side.
    """
    allowed_extensions: List[str] = Field(default_factory=list, description="Accepted file extensions, e.g. ['.mov', '.exr']")
    naming_pattern_is_regex: bool = Field(False, description="If true, naming_pattern is a raw regular expression instead of a token pattern")
    naming_pattern: Optional[str] = Field(
        None,
        description="Either a token pattern (using {name}, {task_type}, {task_name}, {project_name}, "
                    "{project_code}, {version}) or, if naming_pattern_is_regex is set, a raw regular expression"
    )
    check_naming: bool = Field(True, description="Whether the naming pattern is actively checked")
    required: bool = Field(False, description="Whether a submission is expected for this task type (informational only)")
    check_movie_spec: bool = Field(False, description="Whether to check video submissions against movie_resolution/movie_format/movie_codec/movie_frame_rate")
    movie_resolution: Optional[str] = Field(None, description="Required video resolution, e.g. '1920x1080'")
    movie_format: Optional[str] = Field(None, description="Required video format, e.g. 'mov'")
    movie_codec: Optional[str] = Field(None, description="Required video codec, e.g. 'h264'")
    movie_frame_rate: Optional[float] = Field(None, description="Required video frame rate, e.g. 23.976")

    @validator('allowed_extensions')
    def validate_allowed_extensions(cls, v):
        normalized = []
        for ext in v:
            ext = ext.lower()
            if not ext.startswith('.'):
                ext = f'.{ext}'
            if ext not in file_handler.SUPPORTED_FORMATS:
                raise ValueError(
                    f"Extension '{ext}' is not a supported file format. "
                    f"Supported formats: {', '.join(sorted(file_handler.SUPPORTED_FORMATS))}"
                )
            normalized.append(ext)
        return normalized

    @validator('naming_pattern')
    def validate_naming_pattern(cls, v, values):
        if v is None or v == '':
            return None
        if values.get('naming_pattern_is_regex'):
            try:
                re.compile(v)
            except re.error as e:
                raise ValueError(f'Invalid regular expression: {e}')
            return v
        # Token mode: strip out known tokens, then everything left must be safe literal characters
        stripped = NAMING_PATTERN_TOKEN_RE.sub('', v)
        if not NAMING_PATTERN_ALLOWED_CHARS_RE.match(stripped):
            raise ValueError(
                'Naming pattern may only contain letters, numbers, "_", "-", "." '
                'and the tokens {name}, {task_type}, {task_name}, {project_name}, {project_code}, {version}'
            )
        return v

    @validator('movie_resolution')
    def validate_movie_resolution(cls, v):
        if v is None or v == '':
            return None
        if not RESOLUTION_RE.match(v):
            raise ValueError('Movie resolution must be in format "WIDTHxHEIGHT" (e.g., "1920x1080")')
        return v

    @validator('movie_format')
    def validate_movie_format(cls, v):
        if v is None or v == '':
            return None
        v = v.lower().lstrip('.')
        if v not in MOVIE_FORMATS:
            raise ValueError(f'Movie format must be one of: {", ".join(sorted(MOVIE_FORMATS))}')
        return v

    @validator('movie_codec')
    def validate_movie_codec(cls, v):
        if v is None or v == '':
            return None
        v = v.lower()
        if v not in MOVIE_CODECS:
            raise ValueError(f'Movie codec must be one of: {", ".join(sorted(MOVIE_CODECS))}')
        return v

    @validator('movie_frame_rate')
    def validate_movie_frame_rate(cls, v):
        if v is None:
            return None
        if not any(abs(v - rate) < 0.001 for rate in MOVIE_FRAME_RATES):
            raise ValueError(f'Movie frame rate must be one of: {", ".join(str(r) for r in sorted(MOVIE_FRAME_RATES))}')
        return v


class ProjectSubmissionConfig(BaseModel):
    """Submission configuration for a project, keyed by task type"""
    config_by_task_type: Dict[str, SubmissionTypeConfig] = Field(default_factory=dict)
