import traceback

import unreal


MAP_PATH = "/Game/HyperTwistTraining/Maps/L_HyperTwist_ClassicTraining"
SEQUENCE_ROOT = "/Game/HyperTwistTraining/Sequences"
SEQUENCE_PATH = f"{SEQUENCE_ROOT}/LS_HyperTwist_ClassicReplayCapture"
CAMERA_LABEL = "HT_ClassicCubeReplayCaptureCamera"
AUTHOR_TAG = "HyperTwistClassicCubeReplayExport"
SEQUENCE_DURATION_FRAMES = 36000
SEQUENCE_FRAME_RATE = 30


def log(message: str) -> None:
    unreal.log(f"[HyperTwistClassicCubeExportAuthoring] {message}")


def ensure_directory(directory_path: str) -> None:
    if not unreal.EditorAssetLibrary.does_directory_exist(directory_path):
        if not unreal.EditorAssetLibrary.make_directory(directory_path):
            raise RuntimeError(f"Failed to create content directory: {directory_path}")


def require_map_loaded(level_subsystem) -> None:
    if not level_subsystem.load_level(MAP_PATH):
        raise RuntimeError(f"Failed to load classic training map: {MAP_PATH}")


def get_editor_world():
    editor_subsystem = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
    if editor_subsystem is None:
        raise RuntimeError("UnrealEditorSubsystem was not available.")

    world = editor_subsystem.get_editor_world()
    if world is None:
        raise RuntimeError("Editor world was not available after loading the classic map.")
    return world


def save_current_level_or_raise(level_subsystem, label: str) -> None:
    if not level_subsystem.save_current_level():
        raise RuntimeError(f"Failed to save level after {label}.")
    log(f"Saved current level after {label}")

def configure_camera_actor(camera_actor) -> None:
    if camera_actor is None:
        raise RuntimeError("Level Sequence camera creation returned no camera actor.")

    if hasattr(camera_actor, "set_actor_label"):
        camera_actor.set_actor_label(CAMERA_LABEL)
    existing_tags = list(camera_actor.get_editor_property("tags"))
    author_tag = unreal.Name(AUTHOR_TAG)
    if author_tag not in existing_tags:
        existing_tags.append(author_tag)
    camera_actor.set_editor_property("tags", existing_tags)
    camera_actor.set_actor_location(unreal.Vector(-520.0, -520.0, 280.0), False, False)
    camera_actor.set_actor_rotation(unreal.Rotator(-12.0, 42.0, 0.0), False)

    camera_component = camera_actor.get_cine_camera_component()
    if camera_component is not None:
        camera_component.set_editor_property("current_focal_length", 55.0)
        camera_component.set_editor_property("current_aperture", 4.0)

def split_asset_path(asset_path: str):
    package_path, asset_name = asset_path.rsplit("/", 1)
    return package_path, asset_name


def recreate_sequence_asset():
    if unreal.EditorAssetLibrary.does_asset_exist(SEQUENCE_PATH):
        if not unreal.EditorAssetLibrary.delete_asset(SEQUENCE_PATH):
            raise RuntimeError(f"Failed to delete existing replay capture sequence: {SEQUENCE_PATH}")
        log(f"Deleted existing replay capture sequence {SEQUENCE_PATH}")

    package_path, asset_name = split_asset_path(SEQUENCE_PATH)
    asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
    sequence = asset_tools.create_asset(
        asset_name,
        package_path,
        unreal.LevelSequence,
        unreal.LevelSequenceFactoryNew(),
    )
    if sequence is None:
        raise RuntimeError(f"Failed to create replay capture sequence: {SEQUENCE_PATH}")

    return sequence


def author_sequence() -> None:
    sequence = recreate_sequence_asset()
    sequence.set_playback_start(0)
    sequence.set_playback_end(SEQUENCE_DURATION_FRAMES)

    if hasattr(sequence, "set_display_rate"):
        sequence.set_display_rate(unreal.FrameRate(SEQUENCE_FRAME_RATE, 1))

    if not unreal.LevelSequenceEditorBlueprintLibrary.open_level_sequence(sequence):
        raise RuntimeError(f"Failed to open replay capture sequence: {SEQUENCE_PATH}")

    level_sequence_subsystem = unreal.get_editor_subsystem(
        unreal.LevelSequenceEditorSubsystem
    )
    if level_sequence_subsystem is None:
        raise RuntimeError("LevelSequenceEditorSubsystem was not available.")

    log("Creating spawnable replay capture camera through LevelSequenceEditorSubsystem.")
    created_camera = level_sequence_subsystem.create_camera(spawnable=True)
    if isinstance(created_camera, tuple):
        (_camera_binding, camera_actor) = created_camera
    else:
        camera_actor = created_camera
    configure_camera_actor(camera_actor)
    unreal.LevelSequenceEditorBlueprintLibrary.refresh_current_level_sequence()

    if not unreal.EditorAssetLibrary.save_loaded_asset(sequence, False):
        raise RuntimeError(f"Failed to save replay capture sequence: {SEQUENCE_PATH}")

    log(f"Authored replay capture sequence {SEQUENCE_PATH}")


def main() -> None:
    ensure_directory(SEQUENCE_ROOT)
    log("Ensured replay capture sequence content directory.")

    level_subsystem = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
    if level_subsystem is None:
        raise RuntimeError("LevelEditorSubsystem was not available.")

    log("Loading classic training map for replay export authoring.")
    require_map_loaded(level_subsystem)
    log("Classic training map loaded.")
    get_editor_world()
    log("Editor world resolved.")
    author_sequence()
    log("Classic-cube replay export assets are ready.")


if __name__ == "__main__":
    try:
        main()
    except Exception as error:
        unreal.log_error(
            f"[HyperTwistClassicCubeExportAuthoring] {error}\n{traceback.format_exc()}"
        )
        raise
