"""Modal app for testing the SCAIL-2 LTX-2 training/inference path.

Free-tier friendly: `verify` runs on CPU and `smoke` on a cheap T4, both using
tiny random-init models + synthetic data (no checkpoint, no dataset) so a full run
costs pennies. `train` is the scale-up entrypoint that runs the real trainer once
you upload a checkpoint + preprocessed data to the `scail-data` Volume -- that one
uses a real GPU and is NOT free.

Setup (once):
    pip install modal && modal setup

Run:
    modal run modal/app.py::verify     # CPU plumbing check (~free)
    modal run modal/app.py::smoke      # same checks on a T4 GPU (cents)
    modal run modal/app.py::train --config-rel configs/scail_animation_lora.yaml
"""

import subprocess
from pathlib import Path

import modal

REPO = Path(__file__).parent.parent

# Build the workspace once into the image. ltx-kernels (CUDA-compiled) is excluded
# from the workspace and not needed -- attention falls back to SDPA.
image = (
    modal.Image.debian_slim(python_version="3.11")
    .apt_install("git")
    .pip_install("uv")
    .env({"UV_LINK_MODE": "copy", "UV_PROJECT_ENVIRONMENT": "/root/LTX-2/.venv"})
    .add_local_dir(
        str(REPO),
        "/root/LTX-2",
        copy=True,
        ignore=[".git", ".venv", "**/__pycache__", "**/*.pyc", "outputs", "wandb", "**/.pytest_cache"],
    )
    .run_commands("cd /root/LTX-2 && uv sync --package ltx-trainer")
)

app = modal.App("scail-ltx2", image=image)

# Persistent volume for the (large) checkpoint + preprocessed dataset used by `train`.
data_volume = modal.Volume.from_name("scail-data", create_if_missing=True)

_UV_PY = ["uv", "run", "--package", "ltx-trainer", "python"]


def _run(args: list[str]) -> None:
    subprocess.run([*_UV_PY, *args], cwd="/root/LTX-2", check=True)


@app.function(timeout=1800)
def verify() -> None:
    """Run the SCAIL-2 plumbing checks on CPU (near-free)."""
    _run(["modal/checks.py"])


@app.function(gpu="T4", timeout=1800)
def smoke() -> None:
    """Run the same checks on a T4 GPU to validate the CUDA path (cents)."""
    _run(["modal/checks.py"])


@app.function(gpu="A10G", timeout=60 * 60 * 6, volumes={"/data": data_volume})
def train(config_rel: str = "configs/scail_animation_lora.yaml") -> None:
    """Run the real SCAIL trainer. NOT free -- needs a real checkpoint + data.

    Upload your base checkpoint, Gemma text encoder, and preprocessed data to the
    ``scail-data`` Volume (mounted at /data), and point the config's paths at /data.
    For the full 19B model use a bigger GPU (e.g. gpu="A100") and expect real cost.
    """
    _run(["packages/ltx-trainer/scripts/train.py", config_rel])
    data_volume.commit()


@app.local_entrypoint()
def main(target: str = "verify", config_rel: str = "configs/scail_animation_lora.yaml") -> None:
    if target == "smoke":
        smoke.remote()
    elif target == "train":
        train.remote(config_rel)
    else:
        verify.remote()
