"""Face processing and identify workflow schemas."""

from __future__ import annotations

from datetime import date
from typing import Optional

from pydantic import BaseModel, Field, ConfigDict


class ProcessFacesRequest(BaseModel):
    """Request to process faces in photos."""

    model_config = ConfigDict(protected_namespaces=())

    batch_size: Optional[int] = Field(
        None,
        ge=1,
        description="Maximum number of photos to process (None = all unprocessed)",
    )
    detector_backend: str = Field(
        "retinaface",
        description="DeepFace detector backend (retinaface, mtcnn, opencv, ssd)",
    )
    model_name: str = Field(
        "ArcFace",
        description="DeepFace model name (ArcFace, Facenet, Facenet512, VGG-Face)",
    )


class ProcessFacesResponse(BaseModel):
    """Response after initiating face processing."""

    model_config = ConfigDict(protected_namespaces=())

    job_id: str
    message: str
    batch_size: Optional[int] = None
    detector_backend: str
    model_name: str


class FaceItem(BaseModel):
    """Minimal face item for list views."""

    model_config = ConfigDict(from_attributes=True, protected_namespaces=())

    id: int
    photo_id: int
    quality_score: float
    face_confidence: float
    location: str
    pose_mode: Optional[str] = Field("frontal", description="Pose classification (frontal, profile_left, etc.)")
    excluded: bool = Field(False, description="Whether this face is excluded from identification")


class UnidentifiedFacesQuery(BaseModel):
    """Query params for listing unidentified faces."""

    model_config = ConfigDict(protected_namespaces=())

    page: int = 1
    page_size: int = 50
    min_quality: float = 0.0
    date_from: Optional[date] = None
    date_to: Optional[date] = None
    sort_by: str = Field("quality", description="quality|date_taken|date_added")
    sort_dir: str = Field("desc", description="asc|desc")


class UnidentifiedFacesResponse(BaseModel):
    """Paginated unidentified faces list."""

    model_config = ConfigDict(protected_namespaces=())

    items: list[FaceItem]
    page: int
    page_size: int
    total: int


class SimilarFaceItem(BaseModel):
    """Similar face with similarity score (0-1)."""

    id: int
    photo_id: int
    similarity: float
    location: str
    quality_score: float
    filename: str
    pose_mode: Optional[str] = Field("frontal", description="Pose classification (frontal, profile_left, etc.)")


class SimilarFacesResponse(BaseModel):
    """Response containing similar faces for a given face."""

    model_config = ConfigDict(protected_namespaces=())

    base_face_id: int
    items: list[SimilarFaceItem]


class BatchSimilarityRequest(BaseModel):
    """Request to get similarities between multiple faces."""

    model_config = ConfigDict(protected_namespaces=())

    face_ids: list[int] = Field(..., description="List of face IDs to calculate similarities for")
    min_confidence: float = Field(60.0, ge=0.0, le=100.0, description="Minimum confidence percentage (0-100)")


class FaceSimilarityPair(BaseModel):
    """A pair of similar faces with their similarity score."""

    model_config = ConfigDict(protected_namespaces=())

    face_id_1: int
    face_id_2: int
    similarity: float  # 0-1 range
    confidence_pct: float  # 0-100 range


class BatchSimilarityResponse(BaseModel):
    """Response containing similarities between face pairs."""

    model_config = ConfigDict(protected_namespaces=())

    pairs: list[FaceSimilarityPair] = Field(..., description="List of similar face pairs")


class IdentifyFaceRequest(BaseModel):
    """Identify a face by selecting existing or creating new person."""

    model_config = ConfigDict(protected_namespaces=())

    # Either provide person_id or the fields to create new person
    person_id: Optional[int] = None
    first_name: Optional[str] = None
    last_name: Optional[str] = None
    middle_name: Optional[str] = None
    maiden_name: Optional[str] = None
    date_of_birth: Optional[date] = None
    # Optionally identify a batch of face IDs along with this one
    additional_face_ids: Optional[list[int]] = None


class IdentifyFaceResponse(BaseModel):
    """Result of identify operation."""

    model_config = ConfigDict(protected_namespaces=())

    identified_face_ids: list[int]
    person_id: int
    created_person: bool


class FaceUnmatchResponse(BaseModel):
    """Result of unmatch operation."""

    model_config = ConfigDict(protected_namespaces=())

    face_id: int
    message: str


class BatchUnmatchRequest(BaseModel):
    """Request to batch unmatch multiple faces."""

    model_config = ConfigDict(protected_namespaces=())

    face_ids: list[int] = Field(..., min_items=1)


class BatchUnmatchResponse(BaseModel):
    """Result of batch unmatch operation."""

    model_config = ConfigDict(protected_namespaces=())

    unmatched_face_ids: list[int]
    count: int
    message: str


class PersonFaceItem(BaseModel):
    """Face item for person's faces list (includes photo info)."""

    model_config = ConfigDict(from_attributes=True, protected_namespaces=())

    id: int
    photo_id: int
    photo_path: str
    photo_filename: str
    location: str
    face_confidence: float
    quality_score: float
    detector_backend: str
    model_name: str


class PersonFacesResponse(BaseModel):
    """Response containing all faces for a person."""

    model_config = ConfigDict(protected_namespaces=())

    person_id: int
    items: list[PersonFaceItem]
    total: int


class AutoMatchRequest(BaseModel):
    """Request to start auto-match process."""

    model_config = ConfigDict(protected_namespaces=())

    tolerance: float = Field(0.6, ge=0.0, le=1.0, description="Tolerance threshold (lower = stricter matching)")
    auto_accept: bool = Field(False, description="Enable automatic acceptance of matching faces")
    auto_accept_threshold: float = Field(70.0, ge=0.0, le=100.0, description="Similarity threshold for auto-acceptance (0-100%)")


class AutoMatchFaceItem(BaseModel):
    """Unidentified face match for a person."""

    model_config = ConfigDict(protected_namespaces=())

    id: int
    photo_id: int
    photo_filename: str
    location: str
    quality_score: float
    similarity: float  # Confidence percentage (0-100)
    distance: float
    pose_mode: str = Field("frontal", description="Pose classification (frontal, profile_left, etc.)")


class AutoMatchPersonItem(BaseModel):
    """Person with matches for auto-match workflow."""

    model_config = ConfigDict(protected_namespaces=())

    person_id: int
    person_name: str
    reference_face_id: int
    reference_photo_id: int
    reference_photo_filename: str
    reference_location: str
    reference_pose_mode: str = Field("frontal", description="Reference face pose classification")
    face_count: int  # Number of faces already identified for this person
    matches: list[AutoMatchFaceItem]
    total_matches: int


class AutoMatchPersonSummary(BaseModel):
    """Person summary without matches (for fast initial load)."""

    model_config = ConfigDict(protected_namespaces=())

    person_id: int
    person_name: str
    reference_face_id: int
    reference_photo_id: int
    reference_photo_filename: str
    reference_location: str
    reference_pose_mode: str = Field("frontal", description="Reference face pose classification")
    face_count: int  # Number of faces already identified for this person
    total_matches: int = Field(0, description="Total matches (loaded separately)")


class AutoMatchPeopleResponse(BaseModel):
    """Response containing people list without matches (for fast initial load)."""

    model_config = ConfigDict(protected_namespaces=())

    people: list[AutoMatchPersonSummary]
    total_people: int


class AutoMatchPersonMatchesResponse(BaseModel):
    """Response containing matches for a specific person."""

    model_config = ConfigDict(protected_namespaces=())

    person_id: int
    matches: list[AutoMatchFaceItem]
    total_matches: int


class AutoMatchResponse(BaseModel):
    """Response from auto-match start operation."""

    model_config = ConfigDict(protected_namespaces=())

    people: list[AutoMatchPersonItem]
    total_people: int
    total_matches: int
    auto_accepted: bool = Field(False, description="Whether auto-acceptance was performed")
    auto_accepted_faces: int = Field(0, description="Number of faces automatically accepted")
    skipped_persons: int = Field(0, description="Number of persons skipped (non-frontal reference)")
    skipped_matches: int = Field(0, description="Number of matches skipped (didn't meet criteria)")


class AcceptMatchesRequest(BaseModel):
    """Request to accept matches for a person."""

    model_config = ConfigDict(protected_namespaces=())

    face_ids: list[int] = Field(..., min_items=0, description="Face IDs to identify with this person")


class MaintenanceFaceItem(BaseModel):
    """Face item for maintenance view with person info and file path."""

    model_config = ConfigDict(protected_namespaces=())

    id: int
    photo_id: int
    photo_path: str
    photo_filename: str
    quality_score: float
    person_id: Optional[int] = None
    person_name: Optional[str] = None  # Full name if identified
    excluded: bool


class MaintenanceFacesResponse(BaseModel):
    """Response containing all faces for maintenance."""

    model_config = ConfigDict(protected_namespaces=())

    items: list[MaintenanceFaceItem]
    total: int


class DeleteFacesRequest(BaseModel):
    """Request to delete multiple faces."""

    model_config = ConfigDict(protected_namespaces=())

    face_ids: list[int] = Field(..., min_items=1, description="Face IDs to delete")


class DeleteFacesResponse(BaseModel):
    """Response after deleting faces."""

    model_config = ConfigDict(protected_namespaces=())

    deleted_face_ids: list[int]
    count: int
    message: str
