"""Patient models for Medelement MIS API."""

from typing import Optional

from pydantic import Field, RootModel, field_validator

from .base import (
    Gender,
    MedelementBaseModel,
    PhoneArray,
    validate_date_format,
)

PatientPhoneResponse = str | PhoneArray
"""Phone fields in responses may be either a formatted string or a raw phone array."""


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


class CreatePatientRequest(MedelementBaseModel):
    """Request model for creating a new patient.

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

    name: str = Field(..., min_length=1, description="Patient first name")
    lastname: str = Field(..., min_length=1, description="Patient last name")
    middlename: Optional[str] = Field(None, description="Patient middle name")
    company_code: Optional[int] = Field(None, description="Company code for global API access")
    patient_email: Optional[str] = Field(None, description="Patient email address")
    birthday: Optional[str] = Field(None, description="Birthday in dd.mm.yyyy format")
    gender: Optional[Gender] = Field(None, description="Gender: 1=Female, 2=Male")
    patient_phone_2: Optional[PhoneArray] = Field(
        None, description="Phone as [country_code, city_code, phone]"
    )
    iin: Optional[str] = Field(None, description="Individual identification number")
    source_code: Optional[int] = Field(None, description="Information source code")

    @field_validator("birthday")
    @classmethod
    def validate_birthday(cls, v: str | None) -> str | None:
        return validate_date_format(v)


class UpdatePatientRequest(MedelementBaseModel):
    """Request model for updating an existing patient.

    PUT https://api3.medelement.com/doctor/v1/patient
    """

    profile_code: int = Field(..., description="Patient profile code")
    name: Optional[str] = Field(None, min_length=1, description="Patient first name")
    lastname: Optional[str] = Field(None, min_length=1, description="Patient last name")
    middlename: Optional[str] = Field(None, description="Patient middle name")
    patient_email: Optional[str] = Field(None, description="Patient email address")
    birthday: Optional[str] = Field(None, description="Birthday in dd.mm.yyyy format")
    gender: Optional[Gender] = Field(None, description="Gender: 1=Female, 2=Male")
    patient_phone_2: Optional[PhoneArray] = Field(
        None, description="Phone as [country_code, city_code, phone]"
    )
    iin: Optional[str] = Field(None, description="Individual identification number")
    source_code: Optional[int] = Field(None, description="Information source code")
    percentage_discount: Optional[int] = Field(None, description="Personal discount percent")
    social_status: Optional[int] = Field(None, description="Social status code")
    benefit: Optional[int] = Field(None, description="Concession/benefit code")

    @field_validator("birthday")
    @classmethod
    def validate_birthday(cls, v: str | None) -> str | None:
        return validate_date_format(v)


class SearchPatientRequest(MedelementBaseModel):
    """Request model for searching patients.

    GET https://api3.medelement.com/doctor/v2/patients
    At least one search parameter is required.
    """

    patient_code: Optional[list[int]] = Field(None, description="Array of patient codes")
    email: Optional[list[str]] = Field(None, description="Array of email addresses")
    name: Optional[str] = Field(None, description="First name filter")
    lastname: Optional[str] = Field(None, description="Last name filter")
    middlename: Optional[str] = Field(None, description="Middle name filter")
    gender: Optional[int] = Field(None, description="Gender filter: 1=Female, 2=Male")
    birthday: Optional[str] = Field(None, description="Birthday filter in dd.mm.yyyy format")
    patient_phone_2: Optional[PhoneArray] = Field(
        None, description="Phone filter as [country_code, city_code, phone]"
    )
    skip: Optional[int] = Field(None, ge=0, description="Pagination offset")
    patient_status_code: Optional[int] = Field(None, description="Patient status code filter")

    @field_validator("birthday")
    @classmethod
    def validate_birthday(cls, v: str | None) -> str | None:
        return validate_date_format(v)


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


class CreatePatientResponse(MedelementBaseModel):
    """Response model for patient creation.

    HTTP 201 Created
    """

    profile_code: int = Field(..., description="Created patient's unique profile code")
    name: str
    lastname: str
    middlename: Optional[str] = None
    patient_email: Optional[str] = None
    birthday: Optional[str] = None
    gender: Optional[int] = None
    patient_phone_2: Optional[PatientPhoneResponse] = Field(
        None, description="Formatted phone string or raw phone array"
    )
    iin: Optional[str] = None
    source_code: Optional[int] = None


class UpdatePatientResponse(MedelementBaseModel):
    """Response model for patient update."""

    profile_code: int = Field(..., description="Updated patient's unique profile code")
    name: Optional[str] = None
    lastname: Optional[str] = None
    middlename: Optional[str] = None
    patient_email: Optional[str] = None
    birthday: Optional[str] = None
    gender: Optional[int] = None
    patient_phone_2: Optional[PatientPhoneResponse] = Field(
        None, description="Formatted phone string or raw phone array"
    )
    iin: Optional[str] = None
    source_code: Optional[int] = None
    percentage_discount: Optional[int] = None
    social_status: Optional[int] = None
    benefit: Optional[int] = None


class PatientDetail(MedelementBaseModel):
    """Detailed patient information from GET response.

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

    # Primary identifiers
    profile_code: int = Field(..., alias="PROFILE_CODE")
    name: str = Field(..., alias="NAME")
    lastname: str = Field(..., alias="LASTNAME")
    middlename: Optional[str] = Field(None, alias="MIDDLENAME")
    fullname: Optional[str] = Field(None, alias="FULLNAME")
    profile_lastname: Optional[str] = Field(None, alias="PROFILE_LASTNAME")
    profile_name: Optional[str] = Field(None, alias="PROFILE_NAME")
    profile_middlename: Optional[str] = Field(None, alias="PROFILE_MIDDLENAME")

    # Personal info
    birthday: Optional[str] = Field(None, alias="BIRTHDAY")
    gender: Optional[int] = Field(None, alias="GENDER")
    gender_name: Optional[str] = Field(None, alias="GENDER_NAME")
    iin: Optional[str] = Field(None, alias="IIN")
    is_woman: Optional[bool] = Field(None, alias="is_woman")

    # Status
    removed: Optional[int] = Field(None, alias="REMOVED")
    patient_card_number: Optional[str] = Field(None, alias="PATIENT_CARD_NUMBER")
    percentage_discount: Optional[float] = Field(None, alias="PERCENTAGE_DISCOUNT")
    patient_status_code: Optional[str] = Field(None, alias="PATIENT_STATUS_CODE")
    status_name: Optional[str] = Field(None, alias="STATUS_NAME")

    # Contact info
    patient_passport: Optional[str] = Field(None, alias="PATIENT_PASSPORT")
    patient_phone_1: Optional[PatientPhoneResponse] = Field(None, alias="PATIENT_PHONE_1")
    patient_phone_2: Optional[PatientPhoneResponse] = Field(None, alias="PATIENT_PHONE_2")
    patient_phone_3: Optional[PatientPhoneResponse] = Field(None, alias="PATIENT_PHONE_3")
    patient_phone_4: Optional[PatientPhoneResponse] = Field(None, alias="PATIENT_PHONE_4")
    patient_phone_1_str: Optional[str] = Field(None, alias="PATIENT_PHONE_1_STR")
    patient_phone_2_str: Optional[str] = Field(None, alias="PATIENT_PHONE_2_STR")
    patient_phone_3_str: Optional[str] = Field(None, alias="PATIENT_PHONE_3_STR")
    patient_phone_4_str: Optional[str] = Field(None, alias="PATIENT_PHONE_4_STR")
    phones_str: Optional[str] = Field(None, alias="PHONES_STR")
    patient_address: Optional[str] = Field(None, alias="PATIENT_ADDRESS")
    patient_email: Optional[str] = Field(None, alias="PATIENT_EMAIL")
    full_address: Optional[str] = Field(None, alias="FULL_ADDRESS")
    additional_contact_info: Optional[str] = Field(None, alias="ADDITIONAL_CONTACT_INFO")

    # Location/Living
    region: Optional[str] = Field(None, alias="REGION")
    living: Optional[str] = Field(None, alias="LIVING")
    living_code: Optional[int] = Field(None, alias="LIVING_CODE")
    living_type: Optional[str] = Field(None, alias="LIVING_TYPE")

    # Medical info
    death_date: Optional[str] = Field(None, alias="DEATH_DATE")
    blood_group: Optional[int] = Field(None, alias="BLOOD_GROUP")
    blood_group_str: Optional[str] = Field(None, alias="BLOOD_GROUP_STR")
    rh_factor: Optional[int] = Field(None, alias="RH_FACTOR")
    rh_factor_str: Optional[str] = Field(None, alias="RH_FACTOR_STR")
    rw: Optional[str] = Field(None, alias="RW")
    hiv: Optional[str] = Field(None, alias="HIV")
    patient_ambulatory_count: Optional[int] = Field(None, alias="PATIENT_AMBULATORY_COUNT")

    # Employment/Social
    organization: Optional[str] = Field(None, alias="ORGANIZATION")
    department: Optional[str] = Field(None, alias="DEPARTMENT")
    specialty: Optional[str] = Field(None, alias="SPECIALTY")
    education: Optional[str] = Field(None, alias="EDUCATION")

    # Demographics
    nationality: Optional[str] = Field(None, alias="NATIONALITY")
    marital_status: Optional[str] = Field(None, alias="MARITAL_STATUS")
    social_status: Optional[str] = Field(None, alias="SOCIAL_STATUS")
    changes_in_marital_status: Optional[str] = Field(None, alias="CHANGES_IN_MARITAL_STATUS")

    # Benefits/Insurance
    benefit: Optional[str] = Field(None, alias="BENEFIT")
    contract: Optional[str] = Field(None, alias="CONTRACT")
    bad_type: Optional[str] = Field(None, alias="BAD_TYPE")
    source_code: Optional[int] = Field(None, alias="SOURCE_CODE")

    # Doctor assignment
    doctor_code: Optional[int] = Field(None, alias="DOCTOR_CODE")
    doctor_name: Optional[str] = Field(None, alias="DOCTOR_NAME")
    doctor_fullname: Optional[str] = Field(None, alias="DOCTOR_FULLNAME")
    attachment_organization_name: Optional[str] = Field(None, alias="ATTACHMENT_ORGANIZATION_NAME")

    # Other
    oralman: Optional[int] = Field(None, alias="ORALMAN")
    socdisgroup: Optional[int] = Field(None, alias="SOCDISGROUP")
    wh_notification: Optional[str] = Field(None, alias="WH_NOTIFICATION")
    msngr_language: Optional[str] = Field(None, alias="MSNGR_LANGUAGE")


class SearchPatientItem(MedelementBaseModel):
    """Individual patient item in search results."""

    profile_code: int = Field(..., alias="PROFILE_CODE")
    parent_code: Optional[int] = Field(None, alias="PARENT_CODE")
    name: str = Field(..., alias="NAME")
    lastname: str = Field(..., alias="LASTNAME")
    middlename: Optional[str] = Field(None, alias="MIDDLENAME")
    fullname: Optional[str] = Field(None, alias="FULLNAME")
    birthday: Optional[str] = Field(None, alias="BIRTHDAY")
    gender: Optional[int] = Field(None, alias="GENDER")
    iin: Optional[str] = Field(None, alias="IIN")
    ambulatory_count: Optional[int] = Field(None, alias="AMBULATORY_COUNT")
    created_date: Optional[str] = Field(None, alias="CREATED_DATE")
    removed: Optional[int] = Field(None, alias="REMOVED")
    removed_date: Optional[str] = Field(None, alias="REMOVED_DATE")
    removed_reason: Optional[str] = Field(None, alias="REMOVED_REASON")
    description: Optional[str] = Field(None, alias="DESCRIPTION")
    status_code: Optional[str] = Field(None, alias="STATUS_CODE")
    percentage_discount: Optional[float] = Field(None, alias="PERCENTAGE_DISCOUNT")
    patient_passport: Optional[str] = Field(None, alias="PATIENT_PASSPORT")
    patient_phone_1: Optional[PatientPhoneResponse] = Field(None, alias="PATIENT_PHONE_1")
    patient_phone_2: Optional[PatientPhoneResponse] = Field(None, alias="PATIENT_PHONE_2")
    patient_phone_3: Optional[PatientPhoneResponse] = Field(None, alias="PATIENT_PHONE_3")
    patient_phone_4: Optional[PatientPhoneResponse] = Field(None, alias="PATIENT_PHONE_4")
    phones_str: Optional[str] = Field(None, alias="PHONES_STR")
    patient_address: Optional[str] = Field(None, alias="PATIENT_ADDRESS")
    patient_email: Optional[str] = Field(None, alias="PATIENT_EMAIL")
    patient_card_number: Optional[str] = Field(None, alias="PATIENT_CARD_NUMBER")


class SearchPatientResponse(RootModel[list[SearchPatientItem]]):
    """Response model for patient search.

    Returns array of patients, max 100 records.
    """

    def __iter__(self):
        return iter(self.root)

    def __getitem__(self, index):
        return self.root[index]

    def __len__(self):
        return len(self.root)
