import math
from pathlib import Path

import unreal


MAP_PATH = "/Game/Agrarian/Maps/L_GroundZeroTerrain_Test"
WATER_SOURCE_LABEL = "AGR_GZ_FreshWaterSource_01"
EXPECTED_WATER_LOCATION = unreal.Vector(-7200.0, 10400.0, 0.0)
MAX_WATER_LOCATION_DISTANCE_CM = 500.0
MAX_DRESSING_DISTANCE_CM = 3500.0
FRESH_WATER_MATERIAL = "/Game/Agrarian/Materials/M_AGR_GZ_FreshWater"
STONE_MATERIAL = "/Game/Agrarian/Materials/M_AGR_GZ_Stone_Sandstone"
REQUIRED_COMPONENTS = {
    "Mesh",
    "WaterSurfaceProxy",
    "StoneBankProxy",
    "CollectMarkerProxy",
}
REQUIRED_NEARBY_DRESSING = {
    "AGR_GZ_EnvVar_Water_Surface_01",
    "AGR_GZ_EnvVar_Water_Bank_01",
    "AGR_GZ_EnvVar_Water_Reed_01",
    "AGR_GZ_EnvVar_Water_Reed_02",
}


def get_actor_label(actor):
    try:
        return actor.get_actor_label()
    except Exception:
        return actor.get_name()


def material_path(material):
    if not material:
        return ""
    return material.get_path_name().split(".", 1)[0]


def distance_2d(a, b):
    return math.hypot(a.x - b.x, a.y - b.y)


def component_by_name(actor, component_name):
    for component in actor.get_components_by_class(unreal.StaticMeshComponent):
        if component.get_name() == component_name:
            return component
    return None


def main():
    if not unreal.EditorLevelLibrary.load_level(MAP_PATH):
        raise RuntimeError(f"Could not load map: {MAP_PATH}")

    actors = unreal.EditorLevelLibrary.get_all_level_actors()
    failures = []

    water_sources = [actor for actor in actors if get_actor_label(actor) == WATER_SOURCE_LABEL]
    if len(water_sources) != 1:
        raise RuntimeError(f"Expected exactly one {WATER_SOURCE_LABEL}, found {len(water_sources)}")

    water_source = water_sources[0]
    if not isinstance(water_source, unreal.AgrarianWaterSource):
        failures.append(f"{WATER_SOURCE_LABEL} is not an AgrarianWaterSource")

    components = {
        component_name: component_by_name(water_source, component_name)
        for component_name in REQUIRED_COMPONENTS
    }
    for component_name, component in components.items():
        if component is None:
            failures.append(f"{WATER_SOURCE_LABEL} missing {component_name}")

    water_surface = components.get("WaterSurfaceProxy")
    if water_surface:
        if material_path(water_surface.get_material(0)) != FRESH_WATER_MATERIAL:
            failures.append("WaterSurfaceProxy does not use the freshwater material")
        scale = water_surface.get_editor_property("relative_scale3d")
        if scale.x < 2.0 or scale.y < 1.3:
            failures.append(f"WaterSurfaceProxy scale is too small: {scale}")

    collect_marker = components.get("CollectMarkerProxy")
    if collect_marker and material_path(collect_marker.get_material(0)) != FRESH_WATER_MATERIAL:
        failures.append("CollectMarkerProxy does not use the freshwater material")

    for component_name in ("Mesh", "StoneBankProxy"):
        component = components.get(component_name)
        if component and material_path(component.get_material(0)) != STONE_MATERIAL:
            failures.append(f"{component_name} does not use the sandstone material")

    water_location = water_source.get_actor_location()
    if distance_2d(water_location, EXPECTED_WATER_LOCATION) > MAX_WATER_LOCATION_DISTANCE_CM:
        failures.append(f"{WATER_SOURCE_LABEL} moved too far from drainage-candidate placement: {water_location}")

    nearby_labels = {}
    for actor in actors:
        label = get_actor_label(actor)
        if label in REQUIRED_NEARBY_DRESSING:
            nearby_labels[label] = actor

    missing_dressing = REQUIRED_NEARBY_DRESSING.difference(nearby_labels)
    if missing_dressing:
        failures.append("missing water dressing actors: " + ", ".join(sorted(missing_dressing)))

    for label, actor in nearby_labels.items():
        if distance_2d(water_location, actor.get_actor_location()) > MAX_DRESSING_DISTANCE_CM:
            failures.append(f"{label} is too far from the freshwater source")

    project_root = Path(unreal.Paths.convert_relative_path_to_full(unreal.Paths.project_dir()))
    docs = project_root / "Docs" / "Terrain" / "GroundZeroWaterSourceVisualPass.md"
    roadmap = project_root / "AGRARIAN_DEVELOPMENT_ROADMAP.md"
    for path, snippets in {
        docs: ["Ground Zero Water Source Visual Pass", "WaterSurfaceProxy", "stone bank/edge treatment"],
        roadmap: ["[x] Add a real water-source visual pass", "surface material, edge treatment, scale, and placement"],
    }.items():
        text = path.read_text(encoding="utf-8")
        for snippet in snippets:
            if snippet not in text:
                failures.append(f"{path} missing {snippet!r}")

    if failures:
        raise RuntimeError("Ground Zero water-source visual verification failed: " + "; ".join(failures))

    unreal.log(
        "Ground Zero water-source visual verification complete: "
        f"{WATER_SOURCE_LABEL}, {len(REQUIRED_COMPONENTS)} native visual components, "
        f"{len(REQUIRED_NEARBY_DRESSING)} nearby dressing actors."
    )


main()
