"""Pydantic schemas for the catalog domain (species + varieties).

Response models mirror the dict payloads built in services/catalog.py.
Spacing inputs accept int/float/str — the service's forgiving v1 parser
(empty = default/inherit, invalid = fallback, clamped) owns coercion, so
the schemas deliberately do NOT re-validate them.
"""
from __future__ import annotations

from datetime import datetime
from typing import Literal

from pydantic import BaseModel

SpacingValue = int | float | str | None


class IconSpec(BaseModel):
    """v1 species_icon() shape: emoji char OR svg symbol id."""

    type: Literal["emoji", "svg"]
    char: str | None = None
    id: str | None = None


class SpeciesOut(BaseModel):
    id: int
    name: str
    common_name: str = ""
    description: str = ""
    type: str
    primary_function: str
    plants_per_unit: int | None = None
    space_per_unit_sqft: float | None = None
    featured_image_path: str = ""
    is_archived: bool = False
    created_at: datetime | None = None
    icon: IconSpec
    color: str
    variety_count: int | None = None
    plant_count: int | None = None


class SpeciesCreateResult(SpeciesOut):
    """POST /species result — ``created`` is False on a name-dedupe hit."""

    created: bool


class PlantingRef(BaseModel):
    """A planting growing this species (species detail)."""

    id: int
    name: str
    year_id: int | None = None
    year: int | None = None


class SpacingDefault(BaseModel):
    plants_per_unit: int
    space_per_unit_sqft: float


class VarietyOut(BaseModel):
    id: int
    species_id: int | None = None
    name: str
    description: str = ""
    plants_per_unit: int | None = None
    space_per_unit_sqft: float | None = None
    featured_image_path: str = ""
    is_archived: bool = False
    created_at: datetime | None = None
    species_name: str | None = None
    species_type: str | None = None
    species_primary_function: str | None = None
    effective_plants_per_unit: int
    effective_space_per_unit_sqft: float
    icon: IconSpec
    color: str


class VarietyCreateResult(VarietyOut):
    """POST /varieties result — ``created`` is False on a dedupe hit."""

    created: bool


class VarietyDetail(VarietyOut):
    plant_count: int
    species_default: SpacingDefault | None = None


class SpeciesDetail(SpeciesOut):
    varieties: list[VarietyOut]
    plant_count: int
    plantings: list[PlantingRef]


class SpeciesCreate(BaseModel):
    name: str
    common_name: str = ""
    description: str = ""
    plants_per_unit: SpacingValue = None
    space_per_unit_sqft: SpacingValue = None
    type: str = "plant"
    primary_function: str = "edible"


class SpeciesPatch(BaseModel):
    """PATCH body — only fields the client actually sent are applied
    (routers pass model_dump(exclude_unset=True))."""

    name: str | None = None
    common_name: str | None = None
    description: str | None = None
    type: str | None = None
    primary_function: str | None = None
    plants_per_unit: SpacingValue = None
    space_per_unit_sqft: SpacingValue = None


class SpacingUpdate(BaseModel):
    """POST .../spacing body. For species, blank resets to (1, 1.0); for
    varieties, blank clears the override (inherit species)."""

    plants_per_unit: SpacingValue = None
    space_per_unit_sqft: SpacingValue = None


class VarietyCreate(BaseModel):
    name: str
    species_id: int | None = None
    description: str = ""
    plants_per_unit: SpacingValue = None
    space_per_unit_sqft: SpacingValue = None


class VarietyPatch(BaseModel):
    name: str | None = None
    description: str | None = None
    species_id: int | None = None
    plants_per_unit: SpacingValue = None
    space_per_unit_sqft: SpacingValue = None
