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

from pydantic import Field

from ....records.nassco.adjustment_ring_types import AdjustmentRingType
from ....records.nassco.bench_present_options import BenchPresent
from ....records.nassco.channel_exposures import ChannelExposure
from ....records.nassco.channel_installed import ChannelInstalled
from ....records.nassco.channel_types import ChannelType
from ....records.nassco.chimney_first_materials import ChimneyFirstMaterial
from ....records.nassco.chimney_inflow_infiltration_options import ChimneyII
from ....records.nassco.chimney_present import ChimneyPresent
from ....records.nassco.component_materials import ComponentMaterial
from ....records.nassco.cone_types import ConeTypes
from ....records.nassco.cover_condition import CoverCondition
from ....records.nassco.cover_frame_fit_options import CoverFrameFit
from ....records.nassco.cover_insert_conditions import CoverInsertCondition
from ....records.nassco.cover_materials import CoverMaterial
from ....records.nassco.cover_shapes import CoverShape
from ....records.nassco.cover_types import CoverType
from ....records.nassco.evidence_of_surcharge import EvidenceOfSurcharge
from ....records.nassco.frame_condition import FrameCondition
from ....records.nassco.frame_seal_inflow_options import FrameSealInflow
from ....records.nassco.gps_accuracy_options import GPSAccuracy
from ....records.nassco.hole_diameters import HoleDiameter
from ....records.nassco.inflow_potential_from_runoffs import InflowPotentialFromRunoff
from ....records.nassco.insert_types import InsertType
from ....records.nassco.inspection_levels import InspectionLevel
from ....records.nassco.lining_materials import LiningMaterial
from ....records.nassco.location_codes import LocationCode
from ....records.nassco.macp_access_types import MACPAccessType
from ....records.nassco.macp_inspection_statuses import MACPInspectionStatus
from ....records.nassco.manhole_uses import ManholeUse
from ....records.nassco.material_conditions import MaterialCondition
from ....records.nassco.pipe_conditions import PipeJointCondition
from ....records.nassco.pipe_connection_directions import PipeConnectionDirection
from ....records.nassco.pipe_materials import PipeMaterial
from ....records.nassco.pipe_shapes import PipeShape
from ....records.nassco.pipe_types import PipeType
from ....records.nassco.precleaning_options import PreCleaningOptions
from ....records.nassco.ring_conditions import RingCondition
from ....records.nassco.ring_frame_materials import RingFrameMaterial
from ....records.nassco.seal_conditions import SealCondition
from ....records.nassco.step_materials import StepMaterial
from ....records.nassco.surface_types import SurfaceType
from ....records.nassco.survey_purposes import SurveyPurpose
from ....records.nassco.weather_options import WeatherOptions
from ....utils.xml_models import XMLModel


class SurveyInfo(XMLModel):
    surveyed_by: Annotated[Optional[str], Field(default=None, description="Surveyed By")]
    certificate_number: Annotated[Optional[str], Field(default=None, description="Certificate Number")]
    reviewed_by: Annotated[Optional[str], Field(default=None, description="Reviewed By")]
    reviewer_certificate_no: Annotated[Optional[str], Field(default=None, description="Reviewer Certificate No.")]
    owner: Annotated[Optional[str], Field(default=None, description="Owner")]
    customer: Annotated[Optional[str], Field(default=None, description="Customer")]
    po_number: Annotated[Optional[str], Field(default=None, description="P/O Number")]
    work_order_number: Annotated[Optional[str], Field(default=None, description="Work Order")]
    survey_date: Annotated[Optional[date], Field(default=None, description="Date of Survey")]
    survey_time: Annotated[Optional[time], Field(default=None, description="Time of Survey (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="Weather Code")]
    pre_cleaning: Annotated[Optional[PreCleaningOptions], Field(default=None, description="Pre-Cleaning Code", max_length=1)]
    date_cleaned: Annotated[Optional[date], Field(default=None, description="Date Cleaned (YYYYMMDD)")]
    purpose_of_survey: Annotated[Optional[SurveyPurpose], Field(default=None, description="Purpose of Survey")]
    inspection_level: Annotated[Optional[InspectionLevel], Field(default=None, description="Inspection Level", max_length=1)]
    inspection_status: Annotated[Optional[MACPInspectionStatus], Field(default=None, description="Inspection Status")]
    consequence_of_failure: Annotated[Optional[str], Field(default=None, description="Consequence of Failure")]
    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",
            "survey_time": "Time",
            "sheet_numbers": "Sheet_Number",
            "weather": "Weather",
            "pre_cleaning": "Pre_Cleaning",
            "date_cleaned": "Date_Cleaned",
            "purpose_of_survey": "Purpose_of_Survey",
            "consequence_of_failure": "Consequence_of_Failure",
            "video_name": "Media_Label",
        }
        flatten_fields = ["sheet_numbers"]
        ignore_fields = ["inspection_level", "inspection_status"]

    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,
        }


class Location(XMLModel):
    drainage_area: Annotated[Optional[str], Field(default=None, description="Name given to the drainage area to be provided by the survey customer")]
    access_point_no: Annotated[Optional[str], Field(default=None, description="Ref number for the access piont as given in an established local referencing system.")]
    street: Annotated[Optional[str], Field(default=None, description="Street number and street name of the access point being surveyed")]
    city: Annotated[Optional[str], Field(default=None, description="Name of the city or town ")]
    location_code: Annotated[Optional[LocationCode], Field(default=None, description="PACP code for location of manhole surveyed", max_length=1)]
    inflow_potential_from_runoff: Annotated[Optional[InflowPotentialFromRunoff], Field(default=None, description="Inflow Potential from Runoff", max_length=1)]
    location_details: Annotated[Optional[str], Field(default=None, description="Location Details")]
    surface_type: Annotated[Optional[SurfaceType], Field(default=None, description="Surface Type")]

    class Config:
        element_name_map = {
            "drainage_area": "Drainage_Area",
            "access_point_no": "Pipe_Segment_Reference",
            "street": "Street",
            "city": "City",
            "location_code": "Location_Code",
            "location_details": "Location_Details",
            "surface_type": "Surface_Type",
        }
        ignore_fields = ["inflow_potential_from_runoff", "surface_type"]


class Manhole(XMLModel):
    mh_use: Annotated[Optional[ManholeUse], Field(default=None, description="Use of the pipe system", max_length=2)]
    access_type: Annotated[Optional[MACPAccessType], Field(default=None, description="Type of the structure inspected", max_length=4)]
    year_constructed: Annotated[Optional[int], Field(default=None, ge=1000, le=9999, description="Year Constructed")]
    year_renewed: Annotated[Optional[int], Field(default=None, ge=1000, le=9999, description="Year Renewed")]
    evidence_of_surcharge: Annotated[Optional[EvidenceOfSurcharge], Field(default=None, description="Surcharge is defined as when the water level is above the crown of the pipe.")]
    manual_version: Annotated[Optional[str], Field(default="MACP 7.0", description="Version of the manual used for the survey")]
    length_surveyed: Annotated[Optional[float], Field(default=None, description="Distance actually surveyed")]  # do not fill, it's for backward compatibility
    total_length: Annotated[Optional[float], Field(default=None, description="Total length of the pipe of the manhole")]  # do not fill, it's for backward compatibility

    class Config:
        element_name_map = {
            "mh_use": "Pipe_Use",
            "access_type": "Access_Type",
            "year_constructed": "Year_Contstructed",
            "year_renewed": "Year_Renewed",
            "evidence_of_surcharge": "Evidence_of_Surcharge",
            "total_length": "Total_Length",
            "length_surveyed": "Length_Surveyed",
            "manual_version": "Manual_Version",
            "height": "Height",
            "width": "Width",
            "shape": "Shape",
            "material": "Material",
            "lining_method": "Lining_Method",
            "coating_method": "Coating_Method",
            "pipe_joint_length": "Pipe_Joint_Length",
        }

    def get_extra_fields(self) -> Dict[str, Any]:
        return {
            "height": None,
            "width": None,
            "shape": None,
            "material": None,
            "lining_method": None,
            "coating_method": None,
            "pipe_joint_length": None,
            "manual_version": "MACP 7.0",
        }


class Measurements(XMLModel):
    rim_to_invert: Annotated[Optional[float], Field(default=None, description="Distance from the rim to the invert of the manhole")]
    rim_to_grade: Annotated[Optional[float], Field(default=None, description="Depth between the rim of the manhole and the grade level above the (lowest) outgoing pipe")]
    grade_to_invert: Annotated[Optional[float], Field(default=None, description="Depth between the grade (ground) level and the (lowest) outgoing pipe invert level")]
    rim_to_grade_exposed: Annotated[
        Optional[float], Field(default=None, description="Depth between the rim of the manhole and the grade level at lowest exposed point of the manhole exterior")
    ]
    northing: Annotated[
        Optional[float],
        Field(default=None, description="Y Coordinate - Latitude of the center point of the cover. If value exists, Easting and Coordinate System are also required."),
    ]
    easting: Annotated[Optional[float], Field(default=None, description="X Coordinate - Longitude of the center point of the cover.")]
    elevation: Annotated[Optional[float], Field(default=None, description="Z Coordinate - Elevation of the center point of the cover")]
    coordinate_system: Annotated[Optional[str], Field(default=None, description="Datum or reference system used for the GPS coordinates")]
    vertical_datum: Annotated[
        Optional[str], Field(default=None, description="A specific geodetic system used for the GPS vertical or Z coordinate. If value exists, elevation is also required.")
    ]
    gps_accuracy: Annotated[Optional[GPSAccuracy], Field(default=None, description="Accuracy of the GPS coordinates")]

    class Config:
        element_name_map = {
            "rim_to_invert": "Upstream_MH_Rim_to_Invert",
            "rim_to_grade": "Upstream_MH_Rim_to_Grade",
            "grade_to_invert": "Upstream_MH_Grade_to_Invert",
            "northing": "Upstream_MH_Northing",
            "easting": "Upstream_MH_Easting",
            "elevation": "Upstream_MH_Elevation",
            "coordinate_system": "MH_Coordinate_System",
            "vertical_datum": "MH_Vertical_Depth",
            "gps_accuracy": "GPS_Accuracy",
            "upstream_mh_no": "Upstream_MH_No",
            "downstream_mh_no": "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",
        }
        ignore_fields = ["additional_information", "rim_to_grade_exposed"]

    def get_extra_fields(self) -> Dict[str, Any]:
        return {
            "upstream_mh_no": None,
            "downstream_mh_no": None,
            "downstream_mh_rim_to_invert": None,
            "downstream_mh_rim_to_grade": None,
            "downstream_mh_grade_to_invert": None,
            "downstream_mh_northing": None,
            "downstream_mh_easting": None,
            "downstream_mh_elevation": None,
        }


class Cover(XMLModel):
    type: Annotated[Optional[CoverType], Field(default=None, description="Type of the cover")]
    shape: Annotated[Optional[CoverShape], Field(default=None, description="Shape of the cover")]
    size: Annotated[Optional[float], Field(default=None, description="Size of the cover")]
    center_cover_size: Annotated[Optional[float], Field(default=None, description="Dimension of the removable center cover or lid, if it has a cover with a removable centre lid")]
    size_width: Annotated[Optional[float], Field(default=None, description="Measure from the outer edges of the smaller dimension (width)")]
    cover_material: Annotated[Optional[CoverMaterial], Field(default=None, description="Material of cover use MACP codes for material")]
    hole_diameter: Annotated[Optional[HoleDiameter], Field(default=None, description="Dimensions of the holes measured to the nearest half inch or in millimeters.")]
    hole_number: Annotated[Optional[int], Field(default=None, description="Number of holes in the cover")]
    bearing_surface_diameter: Annotated[Optional[float], Field(default=None, description="Diameter or longer dimesion of the cover")]
    bearing_surface_width: Annotated[Optional[float], Field(default=None, description="Smaller dimension of the cover")]
    cover_frame_fit: Annotated[
        Optional[CoverFrameFit],
        Field(
            default=None,
            description=(
                "The Cover/Frame Fit shall be determined by first ensuring the bearing surfaces of cover "
                "and frame are clean and the cover is centred in the frame with an equally spaced gap around the cover"
            ),
        ),
    ]
    cover_condition: Annotated[Optional[CoverCondition], Field(default=None, description="Structural soundness of the cover")]


class CoverInsert(XMLModel):
    insert_type: Annotated[Optional[InsertType], Field(default=None, description="Type of material of the insert.")]
    cover_insert_condition: Annotated[Optional[CoverInsertCondition], Field(default=None, description="Condition of the insert")]


class CoverAdjustmentRing(XMLModel):
    ring_type: Annotated[Optional[AdjustmentRingType], Field(default=None, description="Type of the adjustment ring")]
    ring_material: Annotated[Optional[RingFrameMaterial], Field(default=None, description="Material of the adjustment ring")]
    ring_condition: Annotated[Optional[RingCondition], Field(default=None, description="Condition of the adjustment ring")]
    ring_height: Annotated[Optional[float], Field(default=None, description="The distance from the top of the rim to the bottom of the adjustment ring.")]


class Frame(XMLModel):
    frame_material: Annotated[Optional[RingFrameMaterial], Field(default=None, description="Material of the frame")]
    bearing_surface_width: Annotated[Optional[float], Field(default=None, description="The width of the bearing surface on which the cover sits.")]
    bearing_surface_depth: Annotated[Optional[float], Field(default=None, description="The distance from the top of the frame rim to the top of the frame bearing surface")]
    clear_opening_diameter: Annotated[
        Optional[float], Field(default=None, description="The inner measurement of the frame bearing surface. For not circular, measure the major axis (the length) of the frame.")
    ]
    clear_opening_width: Annotated[
        Optional[float],
        Field(default=None, description="The inner measurement of the frame bearing surface. For not circular, measure the smaller dimension (width) of the frame."),
    ]
    frame_condition: Annotated[Optional[FrameCondition], Field(default=None, description="Text desciptors to describe the structural soundness of the frame")]
    seal_condition: Annotated[Optional[SealCondition], Field(default=None, description="Condition of the seal")]
    offset_distance: Annotated[Optional[float], Field(default=None, description="Offset Distance from the center of the frame to the center of the cover")]
    seal_inflow: Annotated[Optional[FrameSealInflow], Field(default=None, description="Seal Inflow")]
    frame_depth: Annotated[Optional[float], Field(default=None, description="Depth of the frame from the top of the rim to the bottom of the frame")]


class Chimney(XMLModel):
    chimney_present: Annotated[Optional[ChimneyPresent], Field(default=None, description="Indicates if a chimney is present")]
    first_material: Annotated[Optional[ChimneyFirstMaterial], Field(default=None, description="Material of the first part of the chimney")]
    second_material: Annotated[Optional[ChimneyFirstMaterial], Field(default=None, description="Material of the second part of the chimney")]
    chimney_inflow_infiltration: Annotated[
        Optional[ChimneyII],
        Field(default=None, description="Visible evidence of any infiltration/inflow entering through the chimney below the frame seal to the cone seal during inspection."),
    ]
    clear_opening: Annotated[Optional[float], Field(default=None, description="Diameter of the minimum chimney clear opening")]
    chimney_depth: Annotated[Optional[float], Field(default=None, description="Distance from the top of the rim to the bottom of the chimney")]
    lining_interior: Annotated[Optional[LiningMaterial], Field(default=None, description="The material used to coat or line the interior chimney section.")]
    lining_exterior: Annotated[Optional[LiningMaterial], Field(default=None, description="The material used to coat the exterior chimney section.")]
    chimney_condition: Annotated[Optional[MaterialCondition], Field(default=None, description="Condition of the chimney")]


class Cone(XMLModel):
    type: Annotated[Optional[ConeTypes], Field(default=None, description="Type of the cone")]
    material: Annotated[Optional[ComponentMaterial], Field(default=None, description="Material of the cone")]
    depth: Annotated[Optional[float], Field(default=None, description="Vertical distance from the top of the rim to the bottom of the cone")]
    lining_interior: Annotated[Optional[LiningMaterial], Field(default=None, description="The material used to coat or line the interior cone section.")]
    lining_exterior: Annotated[Optional[LiningMaterial], Field(default=None, description="The material used to coat the exterior cone section.")]
    condition: Annotated[Optional[MaterialCondition], Field(default=None, description="Condition of the cone")]


class Wall(XMLModel):
    diameter: Annotated[Optional[float], Field(default=None, description="Diameter of the manhole")]
    width: Annotated[Optional[float], Field(default=None, description="Smallest dimesion (width)")]
    material: Annotated[Optional[ComponentMaterial], Field(default=None, description="Material of the wall")]
    depth: Annotated[Optional[float], Field(default=None, description="Depth of the wall")]
    lining_interior: Annotated[Optional[LiningMaterial], Field(default=None, description="The material used to coat or line the interior wall section.")]
    lining_exterior: Annotated[Optional[LiningMaterial], Field(default=None, description="The material used to coat the exterior wall section.")]
    condition: Annotated[Optional[MaterialCondition], Field(default=None, description="Condition of the wall")]


class Bench(XMLModel):
    bench_present: Annotated[Optional[BenchPresent], Field(default=None, description="Indicates if a bench is present")]
    bench_material: Annotated[Optional[ComponentMaterial], Field(default=None, description="Material of the bench")]
    bench_lining: Annotated[Optional[LiningMaterial], Field(default=None, description="The material used to coat or line the bench section.")]
    bench_condition: Annotated[Optional[MaterialCondition], Field(default=None, description="Condition of the bench")]


class Channel(XMLModel):
    channel_installed: Annotated[Optional[ChannelInstalled], Field(default=None, description="Indicates if a channel is installed")]
    channel_material: Annotated[Optional[ComponentMaterial], Field(default=None, description="Material of the channel")]
    type: Annotated[Optional[ChannelType], Field(default=None, description="Type of the channel")]
    exposure: Annotated[Optional[ChannelExposure], Field(default=None, description="Exposure of the channel")]
    condition: Annotated[Optional[MaterialCondition], Field(default=None, description="Condition of the channel")]


class Steps(XMLModel):
    number: Annotated[Optional[int], Field(default=None, description="Number of steps in the manhole")]
    material: Annotated[Optional[StepMaterial], Field(default=None, description="Material of the steps")]


class ManholeComponents(XMLModel):
    cover: Annotated[Optional[Cover], Field(default_factory=Cover, description="Cover information")]
    cover_insert: Annotated[Optional[CoverInsert], Field(default_factory=CoverInsert, description="Cover Insert information")]
    cover_adjustment_ring: Annotated[Optional[CoverAdjustmentRing], Field(default_factory=CoverAdjustmentRing, description="Cover Adjustment Ring information")]
    frame: Annotated[Optional[Frame], Field(default_factory=Frame, description="Frame information")]
    chimney: Annotated[Optional[Chimney], Field(default_factory=Chimney, description="Chimney information")]
    cone: Annotated[Optional[Cone], Field(default_factory=Cone, description="Cone information")]
    wall: Annotated[Optional[Wall], Field(default_factory=Wall, description="Wall information")]
    bench: Annotated[Optional[Bench], Field(default_factory=Bench, description="Bench information")]
    channel: Annotated[Optional[Channel], Field(default_factory=Channel, description="Channel information")]
    steps: Annotated[Optional[Steps], Field(default_factory=Steps, description="Steps information")]
    additional_info: Annotated[Optional[str], Field(default=None, description="Any other appropriate details about the manhole components")]


class ManholePipeConnection(XMLModel):
    pipe_number: Annotated[
        Optional[str], Field(default=None, description="A sequential number for pipe connection starting with number one as the outgoing pipe and moving clockwise.")
    ]
    clock_position: Annotated[Optional[int], Field(default=None, description="Clock position of the pipe connection, using the outgoing No. 1 pipe referenced as 6 o'clock")]
    rim_to_invert: Annotated[Optional[float], Field(default=None, description="Distance from the rim to the invert of the pipe connection")]
    direction: Annotated[Optional[PipeConnectionDirection], Field(default=None, description="Direction of the pipe connection")]
    material: Annotated[Optional[PipeMaterial], Field(default=None, description="Material of the pipe connection")]
    shape: Annotated[Optional[PipeShape], Field(default=None, description="Shape of the pipe connection")]
    height: Annotated[Optional[float], Field(default=None, description="Height (Diameter) of the pipe connection")]
    width: Annotated[Optional[float], Field(default=None, description="Width of the pipe connection")]
    pipe_condition: Annotated[Optional[PipeJointCondition], Field(default=None, description="Condition of the pipe connection")]
    pipe_seal_condition: Annotated[Optional[SealCondition], Field(default=None, description="Condition of the pipe seal")]
    pipe_type: Annotated[Optional[PipeType], Field(default=None, description="Type of the pipe connection")]
    structure_id: Annotated[Optional[str], Field(default=None, description="Structure ID of the pipe that connects to the manhole")]
    pipe_comments: Annotated[
        Optional[str], Field(default=None, description="This field can be used by the MACP user to enter any appropriate details about the pipe connection observation survey.")
    ]


class MACPInspectionHeaders(XMLModel):
    survey: Annotated[SurveyInfo, Field(default_factory=SurveyInfo, description="Survey Info")]
    location: Annotated[Location, Field(default_factory=Location, description="Location Info")]
    manhole: Annotated[Manhole, Field(default_factory=Manhole, description="Manhole Info")]
    measurements: Annotated[Measurements, Field(default_factory=Measurements, description="Measurements Info")]
    components: Annotated[ManholeComponents, Field(default_factory=ManholeComponents, description="Manhole Component Observations")]
    pipe_connections: Annotated[List[ManholePipeConnection], Field(default_factory=list, description="List of Pipe Connections")]
    additional_information: Annotated[Optional[str], Field(default=None, description="Any other appropriate details about the manhole surveyed")]

    class Config:
        attribute_fields = []
        ignore_fields = [
            "components",
            "pipe_connections",
        ]
        element_name_map = {
            "survey": "General",
            "location": "Location",
            "manhole": "Pipe",
            "measurements": "Measurements",
            "additional_information": "Additional_Info",
        }

    def model_post_init(self, __context) -> None:
        self.manhole.total_length = self.measurements.rim_to_invert
        self.manhole.length_surveyed = self.measurements.rim_to_invert
