from datetime import date, time
from typing import Annotated, Any, Dict, List, Optional, Union

from pydantic import Field

from ....records.nassco.coating_methods import CoatingMethod
from ....records.nassco.gps_accuracy_options import GPSAccuracy
from ....records.nassco.inspection_statuses import InspectionStatus
from ....records.nassco.inspection_technologies import InspectionTechnology
from ....records.nassco.lining_methods import LiningMethod
from ....records.nassco.location_codes import LocationCode
from ....records.nassco.pipe_materials import PipeMaterial
from ....records.nassco.pipe_shapes import PipeShape
from ....records.nassco.pipe_uses import PipeUse
from ....records.nassco.precleaning_options import PreCleaningOptions
from ....records.nassco.survey_directions import SurveyDirection
from ....records.nassco.survey_purposes import SurveyPurpose
from ....records.nassco.weather_options import WeatherOptions
from ....utils.xml_models import XMLModel


class CustomHeaderField(XMLModel):
    field_name: str = Field(..., description="Name of the custom header field")
    field_value: Optional[str] = Field(default=None, description="Value for the custom header field")


class SurveyInfo(XMLModel):
    surveyed_by: Annotated[Optional[str], Field(default=None, description="Operator/PACP User Name")]
    certificate_number: Annotated[Optional[str], Field(default=None, description="PACP® user certificate number of Individual")]
    reviewed_by: Annotated[Optional[str], Field(default=None, description="Reviewer name")]
    reviewer_certificate_no: Annotated[Optional[str], Field(default=None, description="Reviewer's or post-processor's certificate")]
    survey_date: Annotated[Optional[date], Field(default=None, description="Date of survey (YYYYMMDD)")]
    survey_time: Annotated[Optional[time], Field(default=None, description="Time of survey (24 hr HH:MM)")]
    sheet_numbers: Annotated[Optional[List[str]], Field(default=None, description="Hand-written surveys may continue onto more than one sheet")]
    weather: Annotated[Optional[WeatherOptions], Field(default=None, description="Dry/Heavy rain/Light rain/Snow/Dry weather, wet ground")]
    pre_cleaning: Annotated[Optional[PreCleaningOptions], Field(default=None, description="Heavy cleaning/Light cleaning/No/Rodding")]
    cleaning_date: Annotated[Optional[date], Field(default=None, description="Cleaning date (YYYY-MM-DD)")]
    flow_control: Annotated[Optional[str], Field(default=None, description="How the flow has been controlled during the survey")]
    purpose_of_survey: Annotated[Optional[Union[SurveyPurpose, str]], Field(default=None, description="Primary purpose of the inspection")]
    direction_of_survey: Annotated[Optional[SurveyDirection], Field(default=None, description="Upstream/Downstream")]
    inspection_technology: Annotated[Optional[Union[InspectionTechnology, str]], Field(default=None, description="CCTV/Laser/Sonar/IBAK/IPEK/RITEC")]
    inspection_status: Annotated[Optional[InspectionStatus], Field(default=None, description="Status of inspection")]
    consequence_of_failure: Annotated[Optional[str], Field(default=None, description="Financial, social, and environmental impacts of failure")]
    pressure_value: Annotated[Optional[float], Field(default=None, description="Specified minimum testing pressure (Psi/kPa)")]
    owner: Annotated[Optional[str], Field(default=None, description="Municipality or utility district or industrial/organization")]
    customer: Annotated[Optional[str], Field(default=None, description="Consultant or contractor")]
    po_number: Annotated[Optional[str], Field(default=None, description="Customer's purchase order number")]
    work_order_number: Annotated[Optional[str], Field(default=None, description="Work order reference")]
    video_name: Annotated[Optional[str], Field(default=None, description="Name of the video")]  # Only for backward compatibility, no need to fill this field

    class Config:
        element_name_map = {
            "surveyed_by": "Surveyed_by",
            "certificate_number": "Certificate_No",
            "reviewed_by": "Reviewed_by",
            "reviewer_certificate_no": "Reviewer_Certificate_No",
            "owner": "Owner",
            "customer": "Customer",
            "po_number": "PO_Number",
            "work_order_number": "Work_Order_Number",
            "survey_date": "Date",
            "sheet_numbers": "Sheet_Number",
            "weather": "Weather",
            "pre_cleaning": "Pre_Cleaning",
            "cleaning_date": "Date_Cleaned",
            "flow_control": "Flow_Control",
            "purpose_of_survey": "Purpose_of_Survey",
            "direction_of_survey": "Direction_of_Survey",
            "inspection_technology": "Inspection_Technology_Used",
            "consequence_of_failure": "Consequence_of_Failure",
            "pressure_value": "Pressure_Value",
            "video_name": "Media_Label",
        }
        flatten_fields = ["sheet_numbers"]
        ignore_fields = [
            "inspection_status",
            "survey_time",
        ]

    def get_extra_fields(self) -> Dict[str, Any]:
        return {
            "Inspection_Status": f"{self.inspection_status.abbreviation}={self.inspection_status.label}" if self.inspection_status else None,
            "Time": self.survey_time.strftime("%I:%M:%S %p") if self.survey_time else "12:00:00 AM",
        }


class LocationInfo(XMLModel):
    drainage_area: Annotated[Optional[str], Field(default=None, description="Common name for the drainage area or sewer basin")]
    pipe_segment_reference: Annotated[Optional[str], Field(default=None, description="Unique pipe segment reference number")]
    street: Annotated[Optional[str], Field(default=None, description="Street number and name of the starting access point")]
    city: Annotated[Optional[str], Field(default=None, description="City or town where the pipe is located")]
    location_code: Annotated[Optional[LocationCode], Field(default=None, description="Ground cover above the pipe surveyed")]
    location_details: Annotated[Optional[str], Field(default=None, description="Further details on the location")]

    class Config:
        element_name_map = {
            "drainage_area": "Drainage_Area",
            "pipe_segment_reference": "Pipe_Segment_Reference",
            "street": "Street",
            "city": "City",
            "location_code": "Location_Code",
            "location_details": "Location_Details",
        }

    @property
    def address_html(self) -> str:
        address = ""
        if self.street:
            address = f"{address}{self.street}"
        if self.city:
            address = f"{address}</br>{self.city}"
        if self.location_code:
            address = f"{address}</br>{self.location_code}"
        return address


class PipeInfo(XMLModel):
    pipe_use: Annotated[Optional[Union[PipeUse, str]], Field(default=None, description="Use of the pipe")]
    height: Annotated[Optional[float], Field(default=None, description="Estimated pipe height (diameter)")]
    width: Annotated[Optional[float], Field(default=None, description="Maximum pipe width (if not circular)")]
    shape: Annotated[Optional[PipeShape], Field(default=None, description="Pipe shape")]
    material: Annotated[Optional[PipeMaterial], Field(default=None, description="Predominant pipe material used during original construction")]
    lining_method: Annotated[Optional[Union[LiningMethod, str]], Field(default=None, description="Lining system applied to the pipe")]
    coating_method: Annotated[Optional[CoatingMethod], Field(default=None, description="Protective coating system applied to the pipe")]
    pipe_joint_length: Annotated[Optional[float], Field(default=None, description="Length between joints of an average pipe section")]
    total_length: Annotated[Optional[float], Field(default=None, description="Distance from wall of starting to finishing access point")]
    length_surveyed: Annotated[Optional[float], Field(default=None, description="Distance actually surveyed")]
    year_constructed: Annotated[Optional[int], Field(default=None, description="Year the pipe was constructed")]
    year_renewed: Annotated[Optional[int], Field(default=None, description="Year the pipe was rehabilitated/re-lined")]
    manual_version: Annotated[Optional[str], Field(default="PACP 7.0", description="Version of the manual used for the survey")]

    class Config:
        element_name_map = {
            "height": "Height",
            "width": "Width",
            "shape": "Shape",
            "lining_method": "Lining_Method",
            "coating_method": "Coating_Method",
            "pipe_joint_length": "Pipe_Joint_Length",
            "total_length": "Total_Length",
            "length_surveyed": "Length_Surveyed",
            "year_constructed": "Year_Contstructed",
            "year_renewed": "Year_Renewed",
            "manual_version": "Manual_Version",
        }
        ignore_fields = ["material"]

    def get_extra_fields(self) -> Dict[str, Any]:
        pipe_use = self.pipe_use
        if isinstance(pipe_use, PipeUse):
            pipe_use = pipe_use.label

        return {
            "Material": self.material.abbreviation if self.material else None,
            "Pipe_Use": pipe_use,
        }


class Measurements(XMLModel):
    upstream_mh_number: Annotated[Optional[str], Field(default=None, description="Reference number for the upstream access point")]
    upstream_mh_rim_to_invert: Annotated[Optional[float], Field(default=None, description="Distance from rim to invert at upstream manhole")]
    upstream_mh_rim_to_grade: Annotated[Optional[float], Field(default=None, description="Depth from rim to grade at upstream manhole")]
    upstream_mh_grade_to_invert: Annotated[Optional[float], Field(default=None, description="Distance from grade to invert at upstream manhole")]
    upstream_mh_northing: Annotated[Optional[str], Field(default=None, description="Latitude at center of upstream manhole")]
    upstream_mh_easting: Annotated[Optional[str], Field(default=None, description="Longitude at center of upstream manhole")]
    upstream_mh_elevation: Annotated[Optional[str], Field(default=None, description="Elevation at center of upstream manhole")]
    downstream_mh_number: Annotated[Optional[str], Field(default=None, description="Reference number for the downstream access point")]
    downstream_mh_rim_to_invert: Annotated[Optional[float], Field(default=None, description="Distance from rim to invert at downstream manhole")]
    downstream_mh_rim_to_grade: Annotated[Optional[float], Field(default=None, description="Depth from rim to grade at downstream manhole")]
    downstream_mh_grade_to_invert: Annotated[Optional[float], Field(default=None, description="Distance from grade to invert at downstream manhole")]
    downstream_mh_northing: Annotated[Optional[str], Field(default=None, description="Latitude at center of downstream manhole")]
    downstream_mh_easting: Annotated[Optional[str], Field(default=None, description="Longitude at center of downstream manhole")]
    downstream_mh_elevation: Annotated[Optional[str], Field(default=None, description="Elevation at center of downstream manhole")]
    mh_coordinates: Annotated[Optional[str], Field(default=None, description="Coordinates of the manhole in the format 'X,Y'")]
    mh_vertical_datum: Annotated[Optional[str], Field(default=None, description="Vertical datum used for the manhole")]
    gps_accuracy: Annotated[Optional[GPSAccuracy], Field(default=None, description="Accuracy of GPS coordinates")]

    class Config:
        element_name_map = {
            "upstream_mh_number": "Upstream_MH_No",
            "upstream_mh_rim_to_invert": "Upstream_MH_Rim_to_Invert",
            "upstream_mh_rim_to_grade": "Upstream_MH_Rim_to_Grade",
            "upstream_mh_grade_to_invert": "Upstream_MH_Grade_to_Invert",
            "upstream_mh_northing": "Upstream_MH_Northing",
            "upstream_mh_easting": "Upstream_MH_Easting",
            "upstream_mh_elevation": "Upstream_MH_Elevation",
            "downstream_mh_number": "Downstream_MH_No",
            "downstream_mh_rim_to_invert": "Downstream_MH_Rim_to_Invert",
            "downstream_mh_rim_to_grade": "Downstream_MH_Rim_to_Grade",
            "downstream_mh_grade_to_invert": "Downstream_MH_Grade_to_Invert",
            "downstream_mh_northing": "Downstream_MH_Northing",
            "downstream_mh_easting": "Downstream_MH_Easting",
            "downstream_mh_elevation": "Downstream_MH_Elevation",
            "mh_coordinates": "MH_Coordinate_System",
            "mh_vertical_datum": "MH_Vertical_Depth",
            "gps_accuracy": "GPS_Accuracy",
        }


class PACPInspectionHeaders(XMLModel):
    survey: Annotated[SurveyInfo, Field(default_factory=SurveyInfo, description="Survey Info")]
    location: Annotated[LocationInfo, Field(default_factory=LocationInfo, description="Location Info")]
    pipe: Annotated[PipeInfo, Field(default_factory=PipeInfo, description="Pipe Info")]
    measurements: Annotated[Measurements, Field(default_factory=Measurements, description="Measurements Info")]
    additional_info: Annotated[Optional[str], Field(default=None, description="Any other appropriate details about the survey")]
    custom_header_fields: Annotated[Optional[List[CustomHeaderField]], Field(default=None, description="Ten customizable header fields defined per customer")]

    class Config:
        attribute_fields = []
        element_name_map = {
            "survey": "General",
            "location": "Location",
            "pipe": "Pipe",
            "measurements": "Measurements",
            "additional_info": "Additional_Info",
        }
        ignore_fields = ["custom_header_fields"]

    @property
    def section_string(self) -> str:
        """
        Returns the section string for the inspection.
        """

        if self.survey.direction_of_survey:
            if self.survey.direction_of_survey == SurveyDirection.upstream:
                return f"{self.measurements.downstream_mh_number} --- {self.measurements.upstream_mh_number}"
            elif self.survey.direction_of_survey == SurveyDirection.downstream:
                return f"{self.measurements.upstream_mh_number} --- {self.measurements.downstream_mh_number}"
        return ""
