"""
Schemas for Test Catalog, Parameters, and Reference Ranges.
"""

from datetime import datetime
from decimal import Decimal
from uuid import UUID

from pydantic import BaseModel, ConfigDict, Field

from app.models.patient import Gender
from app.models.test import CategoryModality, TestResultType

# ── Test Category Schemas ──────────────────────────────────────────────────────


class TestCategoryBase(BaseModel):
    """Base fields for test category."""

    name: str = Field(..., min_length=1, max_length=200, description="Category name (e.g. Hematology)")
    description: str | None = Field(None, description="Clinical/catalog description")
    modality: CategoryModality = Field(
        default=CategoryModality.PATHOLOGY,
        description="Work queue routing: pathology (specimen lab) or imaging (radiology narrative)",
    )
    is_active: bool = Field(True, description="Whether category is active")


class TestCategoryCreate(TestCategoryBase):
    """Payload to create a new category."""

    lab_id: UUID | None = Field(default=None, description="Branch lab ID (defaults to primary if omitted)")


class TestCategoryUpdate(BaseModel):
    """Payload to update an existing category."""

    name: str | None = Field(None, min_length=1, max_length=200)
    description: str | None = None
    modality: CategoryModality | None = Field(None, description="Work queue routing: pathology or imaging")
    is_active: bool | None = None


class TestCategoryResponse(BaseModel):
    """Response schema for test category."""

    id: UUID
    lab_id: UUID
    name: str
    description: str | None = None
    modality: CategoryModality = CategoryModality.PATHOLOGY
    is_active: bool
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# ── Reference Range Schemas ───────────────────────────────────────────────────


class TestParameterRangeBase(BaseModel):
    """Base fields for parameter reference range."""

    gender: Gender | None = Field(None, description="Gender or None for all")
    age_min: int | None = Field(None, ge=0, description="Minimum age in years")
    age_max: int | None = Field(None, ge=0, description="Maximum age in years")
    numeric_low: Decimal | None = Field(None, description="Lower normal bound")
    numeric_high: Decimal | None = Field(None, description="Upper normal bound")
    text_normal: str | None = Field(None, max_length=200, description="Normal string for text results")


class TestParameterRangeCreate(TestParameterRangeBase):
    """Payload to create a reference range."""

    pass


class TestParameterRangeUpdate(BaseModel):
    """Payload to update a reference range."""

    gender: Gender | None = None
    age_min: int | None = None
    age_max: int | None = None
    numeric_low: Decimal | None = None
    numeric_high: Decimal | None = None
    text_normal: str | None = None


class TestParameterRangeResponse(BaseModel):
    """Response schema for a reference range."""

    id: UUID
    parameter_id: UUID
    gender: Gender | None = None
    age_min: int | None = None
    age_max: int | None = None
    numeric_low: Decimal | None = None
    numeric_high: Decimal | None = None
    text_normal: str | None = None
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# ── Test Parameter Schemas ────────────────────────────────────────────────────


class TestParameterBase(BaseModel):
    """Base fields for a test parameter."""

    name: str = Field(..., min_length=1, max_length=200, description="Parameter name")
    unit: str | None = Field(None, max_length=50, description="Unit of measurement")
    price: Decimal | None = Field(None, ge=0, description="Individual price in NPR if ordered separately")
    decimal_places: int = Field(2, ge=0, le=6, description="Decimal places for reporting")
    calc_factor: Decimal | None = Field(None, description="Calculation multiplier")
    method_note: str | None = Field(None, description="Methodology note")
    sort_order: int = Field(0, description="Display order")
    is_active: bool = Field(True, description="Active status")


class TestParameterCreate(TestParameterBase):
    """Payload to create a test parameter."""

    pass


class TestParameterUpdate(BaseModel):
    """Payload to update a test parameter."""

    name: str | None = Field(None, min_length=1, max_length=200)
    unit: str | None = Field(None, max_length=50)
    price: Decimal | None = Field(None, ge=0)
    decimal_places: int | None = Field(None, ge=0, le=6)
    calc_factor: Decimal | None = None
    method_note: str | None = None
    sort_order: int | None = None
    is_active: bool | None = None


class TestParameterResponse(BaseModel):
    """Response schema for a test parameter."""

    id: UUID
    test_id: UUID
    name: str
    unit: str | None = None
    price: Decimal | None = None
    decimal_places: int
    calc_factor: Decimal | None = None
    method_note: str | None = None
    sort_order: int
    is_active: bool
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# ── Bulk Seed Schemas ───────────────────────────────────────────────────────


class SeedParameterRange(BaseModel):
    """One reference range row inside the bulk seed payload."""

    gender: Gender | None = Field(None, description="Gender or None for all")
    age_min: int | None = Field(None, ge=0)
    age_max: int | None = Field(None, ge=0)
    numeric_low: Decimal | None = Field(None)
    numeric_high: Decimal | None = Field(None)
    text_normal: str | None = Field(None, max_length=200)


class SeedParameter(BaseModel):
    """One analyte inside the bulk seed payload."""

    name: str = Field(..., min_length=1, max_length=200)
    unit: str | None = Field(None, max_length=50)
    price: Decimal | None = Field(
        None, ge=0, description="Standalone analyte price; members should sum to the panel price"
    )
    ranges: list[SeedParameterRange] = Field(default_factory=list)


class SeedTest(BaseModel):
    """One billable panel inside the bulk seed payload."""

    code: str = Field(..., min_length=1, max_length=50)
    name: str = Field(..., min_length=1, max_length=200)
    price: Decimal = Field(..., ge=0)
    result_type: TestResultType = Field(...)
    description: str | None = Field(None)
    parameters: list[SeedParameter] = Field(default_factory=list)


class SeedCategory(BaseModel):
    """One department inside the bulk seed payload."""

    name: str = Field(..., min_length=1, max_length=200)
    description: str | None = Field(None)
    tests: list[SeedTest] = Field(default_factory=list)


class SeedCatalogRequest(BaseModel):
    """Idempotent full-catalog upsert payload for POST /tests/seed."""

    lab_id: UUID | None = Field(default=None, description="Branch lab ID (defaults to primary if omitted)")
    categories: list[SeedCategory] = Field(..., min_length=1)


# ── Test Schemas ──────────────────────────────────────────────────────────────


class TestBase(BaseModel):
    """Base fields for a diagnostic test."""

    name: str = Field(..., min_length=1, max_length=200, description="Test name")
    code: str = Field(..., min_length=1, max_length=50, description="Test code e.g. CBC")
    description: str | None = Field(None, description="Clinical description")
    price: Decimal = Field(..., ge=0, description="Price in NPR")
    result_type: TestResultType = Field(..., description="Result entry format")
    is_active: bool = Field(True, description="Whether test is active")


class TestCreate(TestBase):
    """Payload to create a new test."""

    lab_id: UUID | None = Field(default=None, description="Branch lab ID (defaults to primary if omitted)")
    category_id: UUID = Field(..., description="Category ID")


class TestUpdate(BaseModel):
    """Payload to update an existing test."""

    category_id: UUID | None = None
    name: str | None = Field(None, min_length=1, max_length=200)
    code: str | None = Field(None, min_length=1, max_length=50)
    description: str | None = None
    price: Decimal | None = Field(None, ge=0)
    result_type: TestResultType | None = None
    is_active: bool | None = None


class TestResponse(BaseModel):
    """Response schema for a test."""

    id: UUID
    lab_id: UUID
    category_id: UUID
    category_name: str | None = None
    name: str
    code: str
    description: str | None = None
    price: Decimal
    result_type: TestResultType
    is_active: bool
    parameters: list[TestParameterResponse] = Field(default_factory=list)
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


class TestDetailResponse(TestResponse):
    """Detailed test response including parameters."""

    parameters: list[TestParameterResponse] = Field(default_factory=list)
