import unreal


EXPECTED = {
    "/Game/Agrarian/Blueprints/Resources/BP_WoodResourceNode": {
        "properties": {
            "remaining_harvests": 16,
            "quantity_per_harvest": 2,
            "required_tool_item_id": "basic_tool",
            "allow_bare_hand_gathering": True,
            "tool_quantity_bonus": 1,
            "respawns_for_mvp": True,
            "respawn_delay_seconds": 900.0,
            "max_harvests": 16,
        },
        "yield_item_id": "wood",
    },
    "/Game/Agrarian/Blueprints/Resources/BP_FiberResourceNode": {
        "properties": {
            "remaining_harvests": 10,
            "quantity_per_harvest": 3,
            "required_tool_item_id": "basic_tool",
            "allow_bare_hand_gathering": True,
            "tool_quantity_bonus": 1,
            "respawns_for_mvp": True,
            "respawn_delay_seconds": 600.0,
            "max_harvests": 10,
        },
        "yield_item_id": "fiber",
    },
    "/Game/Agrarian/Blueprints/Resources/BP_StoneResourceNode": {
        "properties": {
            "remaining_harvests": 12,
            "quantity_per_harvest": 2,
            "required_tool_item_id": "basic_tool",
            "allow_bare_hand_gathering": True,
            "tool_quantity_bonus": 1,
            "respawns_for_mvp": False,
            "respawn_delay_seconds": 1800.0,
            "max_harvests": 12,
        },
        "yield_item_id": "stone",
    },
    "/Game/Agrarian/Blueprints/Resources/BP_EdiblePlantResourceNode": {
        "properties": {
            "remaining_harvests": 8,
            "quantity_per_harvest": 1,
            "required_tool_item_id": "None",
            "allow_bare_hand_gathering": True,
            "tool_quantity_bonus": 0,
            "respawns_for_mvp": True,
            "respawn_delay_seconds": 1200.0,
            "max_harvests": 8,
        },
        "yield_item_id": "food",
    },
    "/Game/Agrarian/Blueprints/Structures/BP_Campfire": {
        "properties": {
            "fuel_seconds": 180.0,
            "warmth_radius": 650.0,
            "warmth_per_second": 0.03,
        },
    },
    "/Game/Agrarian/Blueprints/World/BP_FreshWaterSource": {
        "properties": {
            "water_restore_amount": 45.0,
        },
    },
    "/Game/Agrarian/Blueprints/Structures/BP_PrimitiveShelter": {
        "properties": {
            "weather_protection": 0.7,
        },
    },
    "/Game/Agrarian/Blueprints/Wildlife/BP_RabbitWildlife": {
        "properties": {
            "wildlife_id": "rabbit",
            "max_health": 12.0,
            "health": 12.0,
            "wander_radius": 900.0,
            "wander_speed": 160.0,
            "flee_speed": 520.0,
            "aggro_radius": 0.0,
            "flee_radius": 750.0,
            "decision_interval_seconds": 1.5,
        },
        "harvest_yield_ids": ["meat", "hide"],
    },
}


def nearly_equal(left, right):
    if isinstance(left, float) or isinstance(right, float):
        return abs(float(left) - float(right)) < 0.001
    return left == right


def get_property(cdo, property_name, expected_value):
    property_names = [property_name]
    if isinstance(expected_value, bool):
        property_names.append(f"b_{property_name}")
        property_names.append("b" + "".join(part.capitalize() for part in property_name.split("_")))

    for reflected_name in property_names:
        try:
            return cdo.get_editor_property(reflected_name)
        except Exception:
            continue

    raise RuntimeError(f"Could not read property {property_name}")


def main():
    failures = []

    for path, expected in EXPECTED.items():
        blueprint = unreal.EditorAssetLibrary.load_asset(path)
        if not blueprint:
            failures.append(f"{path} missing")
            continue

        generated_class = unreal.EditorAssetLibrary.load_blueprint_class(path)
        if not generated_class:
            failures.append(f"{path} parent class mismatch")
            continue

        cdo = unreal.get_default_object(generated_class)
        for property_name, expected_value in expected.get("properties", {}).items():
            actual_value = get_property(cdo, property_name, expected_value)
            if str(actual_value) != expected_value and not nearly_equal(actual_value, expected_value):
                failures.append(f"{path} {property_name} expected {expected_value}, got {actual_value}")

        expected_yield_item_id = expected.get("yield_item_id")
        if expected_yield_item_id:
            item_asset = cdo.get_editor_property("yield_item_definition")
            definition = item_asset.get_editor_property("definition") if item_asset else None
            item_id = str(definition.get_editor_property("item_id")) if definition else ""
            if item_id != expected_yield_item_id:
                failures.append(f"{path} yield_item_definition expected {expected_yield_item_id}, got {item_id}")

        expected_harvest_ids = expected.get("harvest_yield_ids")
        if expected_harvest_ids:
            harvest_yields = cdo.get_editor_property("harvest_yields")
            actual_ids = [str(stack.get_editor_property("item_id")) for stack in harvest_yields]
            if actual_ids != expected_harvest_ids:
                failures.append(f"{path} harvest_yields expected {expected_harvest_ids}, got {actual_ids}")

    if failures:
        raise RuntimeError("Playable Blueprint verification failed: " + "; ".join(failures))

    unreal.log("Agrarian playable Blueprint verification complete.")


main()
