from dataclasses import dataclass
from typing import List, Optional, Union

from .common import DescriptionV1, Measurement


@dataclass
class EnergyElement:
    # Descriptions revert to localised objects in schema 21.0.1 (were plain strings in 21.0.0)
    description: DescriptionV1
    energy_efficiency_rating: int
    environmental_efficiency_rating: int


@dataclass
class Addendum:
    addendum_numbers: List[int]
    stone_walls: Optional[str] = None
    system_build: Optional[str] = None


@dataclass
class ShowerOutlet:
    shower_wwhrs: int
    shower_outlet_type: int


@dataclass
class ShowerOutlets:
    shower_outlet: ShowerOutlet


@dataclass
class InstantaneousWwhrs:
    """References WWHRS product index numbers (introduced in 21.0.0)."""

    wwhrs_index_number1: Optional[int] = None
    wwhrs_index_number2: Optional[int] = None


@dataclass
class MainHeatingDetail:
    has_fghrs: str  # TODO: make bool
    main_fuel_type: int
    heat_emitter_type: int
    emitter_temperature: Union[int, str]
    main_heating_number: int
    main_heating_control: int
    main_heating_category: int
    main_heating_fraction: int
    main_heating_data_source: int
    boiler_flue_type: Optional[int] = None
    fan_flue_present: Optional[str] = None # TODO: make bool
    boiler_ignition_type: Optional[int] = None
    central_heating_pump_age: Optional[int] = None
    main_heating_index_number: Optional[int] = None
    sap_main_heating_code: Optional[int] = None


@dataclass
class SapHeating:
    cylinder_size: int
    water_heating_code: int
    water_heating_fuel: int
    instantaneous_wwhrs: InstantaneousWwhrs
    main_heating_details: List[MainHeatingDetail]
    immersion_heating_type: Union[int, str]
    has_fixed_air_conditioning: str
    shower_outlets: Optional[ShowerOutlets] = None
    cylinder_insulation_type: Optional[int] = None
    cylinder_thermostat: Optional[str] = None
    secondary_fuel_type: Optional[int] = None
    secondary_heating_type: Optional[int] = None
    cylinder_insulation_thickness: Optional[int] = None


@dataclass
class PvBattery:
    battery_capacity: float


@dataclass
class PvBatteries:
    pv_battery: PvBattery


@dataclass
class WindTurbineDetails:
    hub_height: float
    rotor_diameter: float


@dataclass
class PhotovoltaicSupplyNoneOrNoDetails:
    percent_roof_area: int


@dataclass
class PhotovoltaicSupply:
    none_or_no_details: PhotovoltaicSupplyNoneOrNoDetails


@dataclass
class SapEnergySource:
    mains_gas: str
    meter_type: int
    pv_connection: int
    pv_battery_count: int
    photovoltaic_supply: PhotovoltaicSupply
    wind_turbines_count: int
    wind_turbine_details: WindTurbineDetails
    gas_smart_meter_present: str
    is_dwelling_export_capable: str
    wind_turbines_terrain_type: int
    electricity_smart_meter_present: str
    pv_batteries: Optional[PvBatteries] = None


@dataclass
class WindowTransmissionDetails:
    u_value: float
    data_source: int
    solar_transmittance: float


@dataclass
class SapWindow:
    pvc_frame: str
    glazing_gap: int
    orientation: int
    window_type: int
    frame_factor: float
    glazing_type: int
    window_width: float
    window_height: float
    draught_proofed: str  # TODO: make bool
    window_location: int
    window_wall_type: int
    permanent_shutters_present: str  # TODO: make bool
    window_transmission_details: WindowTransmissionDetails
    permanent_shutters_insulated: str


@dataclass
class SapFloorDimension:
    floor: int
    room_height: Measurement
    total_floor_area: Measurement
    party_wall_length: Union[Measurement, int]
    heat_loss_perimeter: Measurement
    floor_insulation: Optional[int] = None
    floor_construction: Optional[int] = None


@dataclass
class SapRoomInRoof:
    floor_area: Union[int, float]
    construction_age_band: str


@dataclass
class SapAlternativeWall:
    wall_area: float
    wall_dry_lined: str
    wall_construction: int
    wall_insulation_type: int
    wall_thickness_measured: str
    wall_insulation_thickness: Optional[str] = None


@dataclass
class SapBuildingPart:
    identifier: str
    wall_dry_lined: str
    floor_heat_loss: int
    roof_construction: int
    wall_construction: int
    building_part_number: int
    sap_floor_dimensions: List[SapFloorDimension]
    wall_insulation_type: int
    construction_age_band: str
    party_wall_construction: Union[int, str]
    wall_thickness_measured: str
    roof_insulation_location: Union[int, str]
    roof_insulation_thickness: Union[str, int]
    sap_room_in_roof: Optional[SapRoomInRoof] = None
    sap_alternative_wall_1: Optional[SapAlternativeWall] = None
    sap_alternative_wall_2: Optional[SapAlternativeWall] = None
    wall_thickness: Optional[int] = None
    wall_insulation_thickness: Optional[str] = None
    floor_insulation_thickness: Optional[str] = None
    flat_roof_insulation_thickness: Optional[Union[str, int]] = None


@dataclass
class SapFlatDetails:
    level: int
    top_storey: str
    flat_location: int
    heat_loss_corridor: int
    storey_count: Optional[int] = None
    # Changed from plain int in 21.0.0 to a Measurement object in 21.0.1
    unheated_corridor_length: Optional[Union[Measurement, int]] = None


@dataclass
class WindowsTransmissionDetails:
    u_value: float
    data_source: int
    solar_transmittance: float


@dataclass
class ImprovementTexts:
    improvement_description: str
    improvement_summary: Optional[str] = None


@dataclass
class ImprovementDetails:
    improvement_number: Optional[int] = None
    improvement_texts: Optional[ImprovementTexts] = None


@dataclass
class SuggestedImprovement:
    sequence: int
    typical_saving: float
    indicative_cost: Union[str, int]
    improvement_type: str
    improvement_details: ImprovementDetails
    improvement_category: int
    energy_performance_rating: int
    environmental_impact_rating: int


@dataclass
class RenewableHeatIncentive:
    water_heating: int
    space_heating_existing_dwelling: int
    impact_of_loft_insulation: Optional[int] = None
    impact_of_cavity_insulation: Optional[int] = None
    impact_of_solid_wall_insulation: Optional[int] = None


@dataclass
class RdSapSchema21_0_1:
    uprn: int
    roofs: List[EnergyElement]
    walls: List[EnergyElement]
    floors: List[EnergyElement]
    status: str
    tenure: int
    window: EnergyElement
    lighting: EnergyElement
    postcode: str
    hot_water: EnergyElement
    post_town: str
    built_form: int
    door_count: int
    region_code: int
    report_type: int
    sap_heating: SapHeating
    sap_version: float
    sap_windows: List[SapWindow]
    schema_type: str
    uprn_source: str
    country_code: str
    main_heating: List[EnergyElement]
    # dwelling_type remains a plain string (not reverted to DescriptionV1) in 21.0.1
    dwelling_type: str
    language_code: int
    pressure_test: int
    property_type: int
    address_line_1: str
    assessment_type: str
    completion_date: str
    inspection_date: str
    wet_rooms_count: int
    extensions_count: int
    measurement_type: int
    total_floor_area: int
    transaction_type: int
    conservatory_type: int
    heated_room_count: int
    registration_date: str
    sap_energy_source: SapEnergySource
    secondary_heating: EnergyElement
    sap_building_parts: List[SapBuildingPart]
    open_chimneys_count: int
    solar_water_heating: str
    habitable_room_count: int
    heating_cost_current: float
    insulated_door_count: int
    co2_emissions_current: float
    energy_rating_average: int
    energy_rating_current: int
    lighting_cost_current: float
    main_heating_controls: List[EnergyElement]
    has_hot_water_cylinder: str
    heating_cost_potential: float
    hot_water_cost_current: float
    insulated_door_u_value: float
    mechanical_ventilation: int
    percent_draughtproofed: int
    suggested_improvements: List[SuggestedImprovement]
    co2_emissions_potential: float
    energy_rating_potential: int
    lighting_cost_potential: float
    schema_version_original: str
    hot_water_cost_potential: float
    renewable_heat_incentive: RenewableHeatIncentive
    draughtproofed_door_count: int
    mechanical_vent_duct_type: int
    windows_transmission_details: WindowsTransmissionDetails
    cfl_fixed_lighting_bulbs_count: int
    energy_consumption_current: int
    has_fixed_air_conditioning: str
    multiple_glazed_proportion: int
    calculation_software_version: str
    energy_consumption_potential: int
    environmental_impact_current: int
    led_fixed_lighting_bulbs_count: int
    mechanical_vent_duct_placement: int
    mechanical_vent_duct_insulation: int
    potential_energy_efficiency_band: str
    pressure_test_certificate_number: int
    mechanical_ventilation_index_number: int
    co2_emissions_current_per_floor_area: int
    current_energy_efficiency_band: str
    environmental_impact_potential: int
    low_energy_fixed_lighting_bulbs_count: int
    mechanical_vent_duct_insulation_level: int
    mechanical_vent_measured_installation: str
    incandescent_fixed_lighting_bulbs_count: int
    sap_flat_details: Optional[SapFlatDetails] = None
    addendum: Optional[Addendum] = None
    address_line_2: Optional[str] = None
    has_heated_separate_conservatory: Optional[str] = None
    fixed_lighting_outlets_count: Optional[int] = None
    low_energy_fixed_lighting_outlets_count: Optional[int] = None
