"""Settings request schemas."""
import datetime
from decimal import Decimal
from typing import List, Literal, Optional, Any, Dict
from uuid import UUID
from pydantic import BaseModel, ConfigDict, Field, field_validator, EmailStr

from src.core.validators import validate_phone
from src.core.constants import CEMETERY_TYPES, ACCENT_COLORS

FeeCategory = Literal["Interment", "Monument", "Administrative", "Surcharge"]


class FeeItemCreateRequest(BaseModel):
    name: str = Field(min_length=2, max_length=200)
    category: FeeCategory
    description: Optional[str] = Field(None, max_length=500)
    unit_price: Decimal = Field(ge=0, max_digits=8, decimal_places=2)
    is_taxable: bool = False
    is_active: bool = True
    sort_order: int = 0

    @field_validator('name', mode='before')
    @classmethod
    def _strip_name(cls, v: str) -> str:
        return v.strip() if isinstance(v, str) else v


class FeeItemUpdateRequest(BaseModel):
    name: Optional[str] = Field(None, min_length=2, max_length=200)
    category: Optional[FeeCategory] = None
    description: Optional[str] = Field(None, max_length=500)
    unit_price: Optional[Decimal] = Field(None, ge=0, max_digits=8, decimal_places=2)
    is_taxable: Optional[bool] = None
    is_active: Optional[bool] = None
    sort_order: Optional[int] = None

    @field_validator('name', mode='before')
    @classmethod
    def _strip_name(cls, v: Optional[str]) -> Optional[str]:
        return v.strip() if isinstance(v, str) else v


class CemeteryProfileUpdateRequest(BaseModel):
    organization_name: Optional[str] = Field(None, min_length=2, max_length=255)
    cemetery_type: Optional[str] = None
    contact_email: Optional[EmailStr] = None
    contact_phone: Optional[str] = None
    address: Optional[str] = Field(None, min_length=5, max_length=500)
    established_year: Optional[int] = Field(None, ge=1600, le=2100)
    config_json: Optional[Dict[str, Any]] = None
    # Branding fields
    public_site_name: Optional[str] = Field(None, min_length=2, max_length=255)
    location_tagline: Optional[str] = Field(None, min_length=2, max_length=500)
    accent_color: Optional[str] = None

    @field_validator('contact_phone', mode='before')
    @classmethod
    def _validate_phone(cls, v: Optional[str]) -> Optional[str]:
        return validate_phone(v)

    @field_validator('cemetery_type', mode='before')
    @classmethod
    def _validate_cemetery_type(cls, v: Optional[str]) -> Optional[str]:
        if v is not None and v not in CEMETERY_TYPES:
            raise ValueError(f"cemetery_type must be one of: {', '.join(sorted(CEMETERY_TYPES))}")
        return v

    @field_validator('accent_color', mode='before')
    @classmethod
    def _validate_accent_color(cls, v: Optional[str]) -> Optional[str]:
        if v is not None and v not in ACCENT_COLORS:
            raise ValueError(f"accent_color must be one of: {', '.join(sorted(ACCENT_COLORS))}")
        return v

    @field_validator('established_year', mode='before')
    @classmethod
    def _validate_year(cls, v: Optional[int]) -> Optional[int]:
        if v is not None:
            current = datetime.date.today().year
            if not (1600 <= v <= current):
                raise ValueError(f"established_year must be between 1600 and {current}")
        return v


class PlotTypeCreateRequest(BaseModel):
    """INDL-51: full validation for plot type create — see PRD Validation Rules table."""
    model_config = ConfigDict(extra="forbid")

    name: str = Field(min_length=1, max_length=100)
    capacity: int = Field(ge=1, le=20)
    default_length_m: Decimal = Field(gt=0, le=999.99, max_digits=5, decimal_places=2)
    default_width_m: Decimal = Field(gt=0, le=999.99, max_digits=5, decimal_places=2)
    default_depth_m: Decimal = Field(gt=0, le=999.99, max_digits=5, decimal_places=2)
    default_price: Decimal = Field(ge=0, le=Decimal("9999999.99"), max_digits=9, decimal_places=2)
    section_ids: List[UUID] = Field(min_length=1)

    @field_validator('name', mode='before')
    @classmethod
    def _strip_name(cls, v: str) -> str:
        return v.strip() if isinstance(v, str) else v


class PlotTypeUpdateRequest(BaseModel):
    """INDL-51: PATCH semantics — only supplied fields are validated/applied."""
    model_config = ConfigDict(extra="forbid")

    name: Optional[str] = Field(None, min_length=1, max_length=100)
    capacity: Optional[int] = Field(None, ge=1, le=20)
    default_length_m: Optional[Decimal] = Field(None, gt=0, le=999.99, max_digits=5, decimal_places=2)
    default_width_m: Optional[Decimal] = Field(None, gt=0, le=999.99, max_digits=5, decimal_places=2)
    default_depth_m: Optional[Decimal] = Field(None, gt=0, le=999.99, max_digits=5, decimal_places=2)
    default_price: Optional[Decimal] = Field(None, ge=0, le=Decimal("9999999.99"), max_digits=9, decimal_places=2)
    section_ids: Optional[List[UUID]] = Field(None, min_length=1)

    @field_validator('name', mode='before')
    @classmethod
    def _strip_name(cls, v: Optional[str]) -> Optional[str]:
        return v.strip() if isinstance(v, str) else v


class GenerateQRRequest(BaseModel):
    model_config = ConfigDict(extra="forbid")

    qr_type: Literal['entrance', 'section', 'plot', 'headstone', 'contract']
    # INDL-47 SEC-04: wide enough to accept legitimate plot_ref formats
    # ("A.12", "B/12", "Row 2 (Old)", "B-204") while still rejecting
    # path-traversal-style payloads. The regex alone can't cleanly express
    # "no '..' anywhere while still allowing single dots", so the substring
    # check is done separately in the validator below. '%' is also excluded
    # from the charset to close percent-encoding bypass attempts (e.g. %2e%2e),
    # and control characters (CR/LF/NUL) are excluded to prevent header/S3-key
    # injection.
    reference_id: str = Field(
        min_length=1,
        max_length=100,
        pattern=r"^[A-Za-z0-9][A-Za-z0-9 ._/()\-]{0,99}$",
    )
    display_label: Optional[str] = Field(None, max_length=255)

    @field_validator('reference_id')
    @classmethod
    def _reject_path_traversal(cls, v: str) -> str:
        if '..' in v:
            raise ValueError("reference_id must not contain '..'")
        return v


class RegenerateAllRequest(BaseModel):
    model_config = ConfigDict(extra="forbid")

    qr_type: Optional[Literal['entrance', 'section', 'plot', 'headstone', 'contract']] = None
