"""High-level simulation helpers for common SimStadt workflows.

Each function wraps run_workflow_with_citygml() with a pre-built parameter dict
for a specific workflow type (PV, heat demand, green water).
"""

from pathlib import Path

from .results import SimStadtResults
from .runner import run_workflow_with_citygml
from .utils import remove_random_id, set_random_seed


def _set_weather_file(meteonorm_filename: str) -> dict[str, str]:
    """Build the params.xml replacement for the weather file."""
    return {"<string>METEONORM_FILE</string>": f"<string>{Path(meteonorm_filename).name}</string>"}


def _set_calculation_mode(calculation_mode: str) -> dict[str, str]:
    allowed = ["HEATING", "COOLING", "HEATING_AND_COOLING"]
    if calculation_mode not in allowed:
        raise ValueError(f"{calculation_mode!r} must be one of {allowed}")
    return {"<string>CALCULATION_MODE</string>": f"<string>{calculation_mode}</string>"}


def _set_refurbishment(geg_ratio: float) -> dict[str, str]:
    return {"<double>GEG_RATIO</double>": f"<double>{geg_ratio}</double>"}


def photovoltaic_simulation(
    citygml_path: Path | str,
    meteonorm_filename: str,
    roof_percentage: float = 100.0,
    pv_share_flat: float = 40,
    pv_share_tilted: float = 50,
    min_roof_insolation: float = 1000,
    hourly_pv: bool = False,
    with_shadows: bool = True,
    description: str = "Photovoltaic",
) -> SimStadtResults:
    """Run a PhotovoltaicPotential workflow.

    Args:
        citygml_path: Path to the CityGML input file.
        meteonorm_filename: Weather file name (basename only, e.g. 'Wuerzburg-hour.csv').
        roof_percentage: Fraction of roof surfaces to include (sampled randomly but reproducibly).
        pv_share_flat: PV coverage [%] on flat roofs.
        pv_share_tilted: PV coverage [%] on tilted roofs.
        min_roof_insolation: Minimum irradiance threshold [kWh/(m²·a)].
        hourly_pv: Whether to request hourly PV output.
        with_shadows: Use the shadow-aware workflow template.
        description: Label stored on the result object.
    """
    citygml_path = Path(citygml_path)
    replaces = _set_weather_file(meteonorm_filename) | {
        "<int>PV_SHARE_FLAT_ROOF</int>": f"<int>{pv_share_flat}</int>",
        "<int>PV_SHARE_TILTED_ROOF</int>": f"<int>{pv_share_tilted}</int>",
        "<int>MIN_ROOF_INSOLATION</int>": f"<int>{min_roof_insolation}</int>",
        "<boolean>HOURLY_PV</boolean>": f"<boolean>{str(hourly_pv).lower()}</boolean>",
    }
    template = "105_PVWithShadow" if with_shadows else "103_PV"
    pv_results = run_workflow_with_citygml(template, citygml_path, replaces=replaces, description=description)

    set_random_seed(remove_random_id(citygml_path.name))
    pv_results._df = pv_results.dataframe.sample(frac=roof_percentage / 100.0)
    return pv_results


def heatdemand_simulation(
    citygml_path: Path | str,
    meteonorm_filename: str,
    with_shadows: bool = True,
    calculation_mode: str = "HEATING",
    refurbishment_ratio: float = 0.0,
    description: str = "Heat demand",
) -> SimStadtResults:
    """Run a HeatDemand workflow.

    Args:
        citygml_path: Path to the CityGML input file.
        meteonorm_filename: Weather file name or full path (basename is used).
        with_shadows: Use the shadow-aware workflow template.
        calculation_mode: One of 'HEATING', 'COOLING', or 'HEATING_AND_COOLING'.
        refurbishment_ratio: GEG refurbishment ratio (0.0 = no refurbishment, 1.0 = full).
        description: Label stored on the result object.
    """
    citygml_path = Path(citygml_path)
    template = "104_HeatDemandWithShadow" if with_shadows else "101_HeatDemand"
    replaces = (
        _set_weather_file(meteonorm_filename)
        | _set_calculation_mode(calculation_mode)
        | _set_refurbishment(refurbishment_ratio)
    )
    return run_workflow_with_citygml(template, citygml_path, replaces=replaces, description=description)


def greenwater_simulation(
    citygml_path: Path | str,
    meteonorm_filename: str,
    irrigation_ratio: float,
    description: str = "Green water",
) -> SimStadtResults:
    """Run a GreenWater workflow.

    Args:
        citygml_path: Path to the CityGML input file.
        meteonorm_filename: Weather file name (basename only).
        irrigation_ratio: 1.0 = always irrigate when needed; 0.0 = no irrigation.
        description: Label stored on the result object.
    """
    citygml_path = Path(citygml_path)
    replaces = _set_weather_file(meteonorm_filename) | {
        "<double>MAX_WATER_STRESS</double>": f"<double>{1 - irrigation_ratio}</double>",
    }
    return run_workflow_with_citygml("102_GreenWater", citygml_path, replaces=replaces, description=description)
