import unreal


BLUEPRINT_ROOT = "/Game/Agrarian/Blueprints"
RESOURCE_FOLDER = f"{BLUEPRINT_ROOT}/Resources"
STRUCTURE_FOLDER = f"{BLUEPRINT_ROOT}/Structures"
WILDLIFE_FOLDER = f"{BLUEPRINT_ROOT}/Wildlife"
WORLD_FOLDER = f"{BLUEPRINT_ROOT}/World"

WOOD_ITEM_PATH = "/Game/Agrarian/DataAssets/Items/DA_Item_Wood"
FIBER_ITEM_PATH = "/Game/Agrarian/DataAssets/Items/DA_Item_Fiber"
STONE_ITEM_PATH = "/Game/Agrarian/DataAssets/Items/DA_Item_Stone"
FOOD_ITEM_PATH = "/Game/Agrarian/DataAssets/Items/DA_Item_Food"

PLACEHOLDER_MESH_FOLDER = "/Game/Agrarian/Environment/PlaceholderMeshes"
PLACEHOLDER_MESH_SOURCES = {
    "SM_AGR_Placeholder_Cube": "/Game/LevelPrototyping/Meshes/SM_Cube",
    "SM_AGR_Placeholder_Cylinder": "/Game/LevelPrototyping/Meshes/SM_Cylinder",
}
PLACEHOLDER_MESHES = {
    name: f"{PLACEHOLDER_MESH_FOLDER}/{name}"
    for name in PLACEHOLDER_MESH_SOURCES
}
MESH_CUBE_PATH = PLACEHOLDER_MESHES["SM_AGR_Placeholder_Cube"]
MESH_CYLINDER_PATH = PLACEHOLDER_MESHES["SM_AGR_Placeholder_Cylinder"]

BLUEPRINTS = [
    {
        "asset": "BP_WoodResourceNode",
        "folder": RESOURCE_FOLDER,
        "parent": unreal.AgrarianResourceNode,
        "defaults": {
            "yield_item_definition": WOOD_ITEM_PATH,
            "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,
        },
        "mesh": MESH_CUBE_PATH,
        "scale": unreal.Vector(1.0, 1.0, 1.5),
    },
    {
        "asset": "BP_FiberResourceNode",
        "folder": RESOURCE_FOLDER,
        "parent": unreal.AgrarianResourceNode,
        "defaults": {
            "yield_item_definition": FIBER_ITEM_PATH,
            "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,
        },
        "mesh": MESH_CYLINDER_PATH,
        "scale": unreal.Vector(0.8, 0.8, 1.0),
    },
    {
        "asset": "BP_StoneResourceNode",
        "folder": RESOURCE_FOLDER,
        "parent": unreal.AgrarianResourceNode,
        "defaults": {
            "yield_item_definition": STONE_ITEM_PATH,
            "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,
        },
        "mesh": MESH_CUBE_PATH,
        "scale": unreal.Vector(0.9, 0.75, 0.45),
    },
    {
        "asset": "BP_EdiblePlantResourceNode",
        "folder": RESOURCE_FOLDER,
        "parent": unreal.AgrarianResourceNode,
        "defaults": {
            "yield_item_definition": FOOD_ITEM_PATH,
            "remaining_harvests": 8,
            "quantity_per_harvest": 1,
            "allow_bare_hand_gathering": True,
            "tool_quantity_bonus": 0,
            "respawns_for_mvp": True,
            "respawn_delay_seconds": 1200.0,
            "max_harvests": 8,
        },
        "mesh": MESH_CYLINDER_PATH,
        "scale": unreal.Vector(0.65, 0.65, 0.85),
    },
    {
        "asset": "BP_Campfire",
        "folder": STRUCTURE_FOLDER,
        "parent": unreal.AgrarianCampfire,
        "defaults": {
            "fuel_seconds": 180.0,
            "warmth_radius": 650.0,
            "warmth_per_second": 0.03,
        },
        "mesh": MESH_CYLINDER_PATH,
        "scale": unreal.Vector(1.3, 1.3, 0.25),
    },
    {
        "asset": "BP_FreshWaterSource",
        "folder": WORLD_FOLDER,
        "parent": unreal.AgrarianWaterSource,
        "defaults": {
            "water_restore_amount": 45.0,
            "display_name": "Fresh Water Spring",
        },
        "mesh": MESH_CYLINDER_PATH,
        "scale": unreal.Vector(2.2, 2.2, 0.12),
    },
    {
        "asset": "BP_PrimitiveShelter",
        "folder": STRUCTURE_FOLDER,
        "parent": unreal.AgrarianShelterActor,
        "defaults": {
            "weather_protection": 0.7,
        },
        "mesh": MESH_CUBE_PATH,
        "scale": unreal.Vector(3.0, 2.0, 1.4),
    },
    {
        "asset": "BP_RabbitWildlife",
        "folder": WILDLIFE_FOLDER,
        "parent": unreal.AgrarianWildlifeBase,
        "defaults": {
            "wildlife_id": "rabbit",
            "display_name": "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_yields": [
                {
                    "item_id": "meat",
                    "display_name": "Meat",
                    "quantity": 1,
                    "unit_weight": 0.5,
                },
                {
                    "item_id": "hide",
                    "display_name": "Hide",
                    "quantity": 2,
                    "unit_weight": 0.7,
                },
            ],
        },
    },
]


def load_required_asset(path):
    asset = unreal.EditorAssetLibrary.load_asset(path)
    if not asset:
        raise RuntimeError(f"Required asset not found: {path}")
    return asset


def ensure_native_placeholder_meshes():
    unreal.EditorAssetLibrary.make_directory(PLACEHOLDER_MESH_FOLDER)
    for asset_name, source_path in PLACEHOLDER_MESH_SOURCES.items():
        destination_path = f"{PLACEHOLDER_MESH_FOLDER}/{asset_name}"
        if unreal.EditorAssetLibrary.does_asset_exist(destination_path):
            continue

        if not unreal.EditorAssetLibrary.duplicate_asset(source_path, destination_path):
            raise RuntimeError(f"Could not create native placeholder mesh {destination_path} from {source_path}")
        unreal.EditorAssetLibrary.save_asset(destination_path)
        unreal.log(f"Created native placeholder mesh: {destination_path}")


def create_or_load_blueprint(asset_name, folder, parent_class):
    path = f"{folder}/{asset_name}"
    existing = unreal.EditorAssetLibrary.load_asset(path)
    if existing:
        return existing

    factory = unreal.BlueprintFactory()
    factory.set_editor_property("parent_class", parent_class)
    asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
    blueprint = asset_tools.create_asset(asset_name, folder, None, factory)
    if not blueprint:
        raise RuntimeError(f"Could not create {path}")
    return blueprint


def make_stack(data):
    stack = unreal.AgrarianItemStack()
    stack.set_editor_property("item_id", data["item_id"])
    stack.set_editor_property("display_name", data["display_name"])
    stack.set_editor_property("quantity", data["quantity"])
    stack.set_editor_property("unit_weight", data["unit_weight"])
    return stack


def set_default_property(cdo, property_name, value):
    property_names = [property_name]
    if isinstance(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:
            cdo.set_editor_property(reflected_name, value)
            return
        except Exception:
            continue

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


def apply_defaults(blueprint, config):
    blueprint_path = f"{config['folder']}/{config['asset']}"
    unreal.BlueprintEditorLibrary.compile_blueprint(blueprint)
    generated_class = unreal.EditorAssetLibrary.load_blueprint_class(blueprint_path)
    if not generated_class:
        raise RuntimeError(f"Could not load generated class for {blueprint_path}")

    cdo = unreal.get_default_object(generated_class)

    for property_name, value in config.get("defaults", {}).items():
        if property_name == "yield_item_definition":
            value = load_required_asset(value)
        elif property_name == "harvest_yields":
            value = [make_stack(stack_data) for stack_data in value]

        set_default_property(cdo, property_name, value)

    mesh_path = config.get("mesh")
    if mesh_path:
        mesh_component = cdo.get_editor_property("mesh")
        mesh_component.set_editor_property("static_mesh", load_required_asset(mesh_path))
        mesh_component.set_editor_property("relative_scale3d", config.get("scale", unreal.Vector(1.0, 1.0, 1.0)))

    unreal.BlueprintEditorLibrary.compile_blueprint(blueprint)
    unreal.EditorAssetLibrary.save_loaded_asset(blueprint)


def main():
    ensure_native_placeholder_meshes()

    for folder in (RESOURCE_FOLDER, STRUCTURE_FOLDER, WILDLIFE_FOLDER, WORLD_FOLDER):
        unreal.EditorAssetLibrary.make_directory(folder)

    for config in BLUEPRINTS:
        blueprint = create_or_load_blueprint(config["asset"], config["folder"], config["parent"])
        apply_defaults(blueprint, config)
        unreal.log(f"Configured Blueprint: {config['folder']}/{config['asset']}")

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


main()
