"""Reception (appointment) models for Medelement MIS API."""

from typing import Optional

from pydantic import Field, field_validator, model_validator

from .base import (
    ColorCode,
    MedelementBaseModel,
    validate_datetime_format,
)


# ============================================================================
# Request Models
# ============================================================================


class CreateReceptionRequest(MedelementBaseModel):
    """Request model for creating a new reception/appointment.

    POST https://api3.medelement.com/v1/doctor/reception
    """

    patient_code: int = Field(..., description="Patient's profile code")
    specialist_code: int = Field(..., description="Specialist/doctor code")
    company_cabinet_code: int = Field(..., description="Cabinet code")
    starttime: str = Field(..., description="Start time in dd.mm.yyyy hh:mm format")
    endtime: str = Field(..., description="End time in dd.mm.yyyy hh:mm format")
    nomenclature_code: Optional[int] = Field(None, description="Service/procedure code")
    description: Optional[str] = Field(None, description="Appointment notes")
    color_code: Optional[ColorCode] = Field(
        None, description="Color code for visual highlighting (0-9)"
    )

    @field_validator("starttime", "endtime")
    @classmethod
    def validate_times(cls, v: str) -> str:
        result = validate_datetime_format(v)
        if result is None:
            raise ValueError("Time must be in dd.mm.yyyy hh:mm format")
        return result


class GetReceptionsRequest(MedelementBaseModel):
    """Request model for getting receptions list.

    POST https://api3.medelement.com/v1/timetable/get_receptions_short
    """

    specialist_code: int = Field(..., alias="specialistCode")
    begin_datetime: str = Field(..., alias="beginDatetime")
    end_datetime: str = Field(..., alias="endDatetime")
    company_cabinet_code: Optional[int] = Field(None, alias="companyCabinetCode")
    skip: Optional[int] = Field(None, ge=0)


class DeleteReceptionRequest(MedelementBaseModel):
    """Request model for deleting a reception.

    POST https://api3.medelement.com/v2/doctor/reception/remove
    Only open, unpaid receptions can be deleted.
    """

    reception_code: int = Field(..., description="Reception code to delete")


class RescheduleReceptionRequest(MedelementBaseModel):
    """Request model for rescheduling a reception.

    POST https://api3.medelement.com/v2/doctor/reception/change_reception_date
    """

    reception_code: int = Field(..., description="Reception code to reschedule")
    doctor_code: int = Field(..., description="Doctor code")
    start_time: str = Field(..., description="New start time in dd.mm.yyyy hh:mm format")
    end_time: str = Field(..., description="New end time in dd.mm.yyyy hh:mm format")

    @field_validator("start_time", "end_time")
    @classmethod
    def validate_times(cls, v: str) -> str:
        result = validate_datetime_format(v)
        if result is None:
            raise ValueError("Time must be in dd.mm.yyyy hh:mm format")
        return result


class SearchReceptionRequest(MedelementBaseModel):
    """Request model for searching receptions.

    POST https://api3.medelement.com/v2/doctor/reception/search
    """

    begin_datetime: str = Field(..., description="Search start datetime")
    end_datetime: str = Field(..., description="Search end datetime")
    patient_code: Optional[int] = Field(None, description="Filter by patient")
    active: Optional[int] = Field(None, ge=0, le=1, description="Filter: 0=Closed, 1=Open")
    removed: Optional[int] = Field(None, ge=0, le=1, description="Filter by removed status")
    paid: Optional[int] = Field(None, ge=0, le=1, description="Filter by paid status")
    skip: Optional[int] = Field(None, ge=0, description="Pagination offset")
    only_ambulator: Optional[int] = Field(None, ge=0, le=1, description="Only ambulatory visits")


class SearchReceptionPDRequest(MedelementBaseModel):
    """Request model for enhanced reception search (v3 API).

    POST https://api3.medelement.com/v3/doctor/reception/search_pd
    """

    begin_datetime: str = Field(..., description="Search start datetime (dd.mm.yyyy)")
    end_datetime: str = Field(..., description="Search end datetime (dd.mm.yyyy)")
    patient_code: Optional[int] = Field(None, description="Filter by patient")
    active: Optional[int] = Field(None, ge=0, le=1, description="Filter: 0=Closed, 1=Open")
    removed: Optional[int] = Field(None, ge=0, le=1, description="Filter by removed status")
    paid: Optional[int] = Field(None, ge=0, le=1, description="Filter by paid status")
    skip: Optional[int] = Field(None, ge=0, description="Pagination offset")


# ============================================================================
# Response Models
# ============================================================================


class CreateReceptionResponse(MedelementBaseModel):
    """Response model for reception creation.

    HTTP 201 Created
    """

    message: str
    reception_code: int


class ReceptionDetail(MedelementBaseModel):
    """Detailed reception information from GET response.

    GET https://api3.medelement.com/v1/doctor/reception/:reception_code
    Uses UPPERCASE field names from API.
    """

    reception_code: int = Field(..., alias="RECEPTION_CODE")
    profile_code: int = Field(..., alias="PROFILE_CODE", description="Patient code")
    starttime: str = Field(..., alias="STARTTIME")
    endtime: str = Field(..., alias="ENDTIME")
    active: int = Field(..., alias="ACTIVE", description="1=Open, 0=Closed")
    removed: int = Field(..., alias="REMOVED")
    company_code: Optional[int] = Field(None, alias="COMPANY_CODE")

    @property
    def is_open(self) -> bool:
        """Check if reception is open."""
        return self.active == 1

    @property
    def is_removed(self) -> bool:
        """Check if reception is removed."""
        return self.removed == 1


class ReceptionListItem(MedelementBaseModel):
    """Reception item in list response (short format).

    From POST /v1/timetable/get_receptions_short
    """

    reception_code: int = Field(..., alias="RECEPTION_CODE")
    patient_code: int = Field(..., alias="PATIENT_CODE")
    starttime: str = Field(..., alias="STARTTIME")
    endtime: str = Field(..., alias="ENDTIME")
    active: int = Field(..., alias="ACTIVE", description="1=Open, 0=Closed")
    removed: int = Field(..., alias="REMOVED")
    paid: Optional[int] = Field(None, alias="PAID")
    reccount: Optional[int] = Field(None, alias="RECCOUNT", description="Total records count")


class GetReceptionsResponse(MedelementBaseModel):
    """Response model for getting receptions list.

    Max 1000 records per request.
    Docs show ``{"receptions": [...]}``, but some integrations may return the
    array directly, so the SDK accepts both payload shapes.
    """

    receptions: list[ReceptionListItem] = Field(default_factory=list)

    @model_validator(mode="before")
    @classmethod
    def unwrap_root_list(cls, value: object) -> object:
        if isinstance(value, list):
            return {"receptions": value}
        return value


class SearchReceptionItem(MedelementBaseModel):
    """Reception item in search results (v2 API)."""

    reception_code: int = Field(..., alias="RECEPTION_CODE")
    patient_code: int = Field(..., alias="PATIENT_CODE")
    starttime: str = Field(..., alias="STARTTIME")
    endtime: str = Field(..., alias="ENDTIME")
    active: int = Field(..., alias="ACTIVE")
    removed: int = Field(..., alias="REMOVED")
    paid: Optional[int] = Field(None, alias="PAID")
    reccount: Optional[int] = Field(None, alias="RECCOUNT", description="Total records count")

    @property
    def is_open(self) -> bool:
        """Check if reception is open."""
        return self.active == 1


class SearchReceptionResponse(MedelementBaseModel):
    """Response model for reception search (v2 API).

    Docs show ``{"receptions": [...]}``, but some integrations may return the
    array directly, so the SDK accepts both payload shapes.
    """

    receptions: list[SearchReceptionItem] = Field(default_factory=list)

    @model_validator(mode="before")
    @classmethod
    def unwrap_root_list(cls, value: object) -> object:
        if isinstance(value, list):
            return {"receptions": value}
        return value


class SearchReceptionPDItem(MedelementBaseModel):
    """Enhanced reception item in search results (v3 API with specialist details)."""

    reception_code: int = Field(..., alias="RECEPTION_CODE")
    patient_code: int = Field(..., alias="PATIENT_CODE")
    starttime: str = Field(..., alias="STARTTIME")
    endtime: str = Field(..., alias="ENDTIME")
    active: int = Field(..., alias="ACTIVE")
    removed: int = Field(..., alias="REMOVED")
    paid: Optional[int] = Field(None, alias="PAID")
    reccount: Optional[int] = Field(None, alias="RECCOUNT")
    notify: Optional[int] = Field(None, alias="NOTIFY")
    specialist_code: Optional[int] = Field(None, alias="SPECIALIST_CODE")
    specialist_fullname: Optional[str] = Field(None, alias="SPECIALIST_FULLNAME")
    cabinet_type_name: Optional[str] = Field(None, alias="CABINET_TYPE_NAME")
    department_code: Optional[int] = Field(None, alias="DEPARTMENT_CODE")

    @property
    def is_open(self) -> bool:
        """Check if reception is open."""
        return self.active == 1


class SearchReceptionPDResponse(MedelementBaseModel):
    """Response model for enhanced reception search (v3 API).

    Docs show ``{"receptions": [...]}``, but some integrations may return the
    array directly, so the SDK accepts both payload shapes.
    """

    receptions: list[SearchReceptionPDItem] = Field(default_factory=list)

    @model_validator(mode="before")
    @classmethod
    def unwrap_root_list(cls, value: object) -> object:
        if isinstance(value, list):
            return {"receptions": value}
        return value
