"""Proof demo generator with evidence validation gate.

This module provides `build_proof_demo()` which composes evidence validation
(from S03) with manifest generation (from M006). The function gates on
`validate_proof_evidence()` — if evidence is invalid, it raises `ValueError`
with the failing checks instead of producing a misleading manifest.

On valid evidence, it delegates to `build_manifest()` using the job-keyed
evidence paths.
"""

from __future__ import annotations

import tempfile
from pathlib import Path

from mcp_vm_blackbox.demo_manifest import ReplayBundle, build_manifest, save_manifest
from mcp_vm_blackbox.job_store import JobStore
from mcp_vm_blackbox.proof_evidence import validate_proof_evidence
from mcp_vm_blackbox.render_video import render_video


async def build_proof_demo(  # noqa: PLR0914
    job_id: str,
    vm_name: str,
    evidence_root: Path,
    save_path: Path | None = None,
    jobs_root: Path | None = None,
    video_enabled: bool = False,
) -> ReplayBundle:
    """Build a demo manifest from validated proof run evidence.

    This function gates on `validate_proof_evidence()` — if any required
    evidence is missing or invalid, it raises `ValueError` with the failing
    check details instead of producing a misleading manifest.

    Args:
        job_id: The job identifier for the proof run.
        vm_name: The VM name associated with the job.
        evidence_root: Root directory for evidence (typically
            `.mcp-vm-blackbox/evidence`).
        save_path: Optional path to save the manifest JSON. If provided,
            the manifest is written to disk.
        jobs_root: Optional root directory for job storage. If not provided,
            derived from `evidence_root / ".." / "jobs"`.
        video_enabled: When True, validates that render_staging directory
            exists and populates render_staging_path on the manifest.

    Returns:
        ReplayBundle manifest built from validated evidence.

    Raises:
        ValueError: If evidence validation fails, with message containing
            the list of failing checks and their details.

    Example:
        >>> manifest = build_proof_demo(
        ...     job_id="abc123", vm_name="Win11-Pro", evidence_root=Path(".mcp-vm-blackbox/evidence")
        ... )
        >>> manifest.primary_job_id
        'abc123'
        >>> manifest.has_continuous_motion
        False
    """
    # Derive jobs_root from evidence_root if not provided.
    # JobStore appends "jobs" internally, so pass the parent of evidence_root
    # (i.e. .mcp-vm-blackbox), NOT evidence_root.parent / "jobs".
    # Passing the already-suffixed path would produce .mcp-vm-blackbox/jobs/jobs.
    if jobs_root is None:
        jobs_root = evidence_root.parent

    # Derive evidence paths following canonical structure:
    # evidence/{vm_name}/{job_id}/screenshots/
    # evidence/{vm_name}/{job_id}/proof.txt
    job_evidence_dir = evidence_root / vm_name / job_id
    screenshots_dir = job_evidence_dir / "screenshots"
    proof_file_path = job_evidence_dir / "proof.txt"

    # Recordings are optional — look in the same location if it exists
    recordings_dir = job_evidence_dir / "recordings"
    if not recordings_dir.exists():
        recordings_dir = None

    # Terminal and TUI captures are optional — include only when present
    terminal_dir = job_evidence_dir / "terminal"
    if not terminal_dir.exists():
        terminal_dir = None

    tui_captures_dir = job_evidence_dir / "tui_captures"
    if not tui_captures_dir.exists():
        tui_captures_dir = None

    # Gate on evidence validation
    validation_result = await validate_proof_evidence(
        job_id=job_id,
        vm_name=vm_name,
        jobs_root=jobs_root,
        screenshots_dir=screenshots_dir,
        proof_file_path=proof_file_path,
        recordings_dir=recordings_dir,
        video_enabled=video_enabled,
    )

    if not validation_result.valid:
        # Format failing checks for the error message
        failing_checks = [check for check in validation_result.checks if not check.passed]
        details = "; ".join(f"{check.name}: {check.detail}" for check in failing_checks)
        msg = f"Invalid proof evidence for job {job_id}: {details}"
        raise ValueError(msg)

    # Get job path from JobStore
    job_store = JobStore(root=jobs_root)
    await job_store.load(job_id, vm_name)
    job_path = job_store._job_path(vm_name, job_id)  # noqa: SLF001

    # Build manifest from validated evidence
    manifest = build_manifest(
        job_id=job_id,
        vm_name=vm_name,
        job_path=job_path,
        screenshots_dir=screenshots_dir,
        recordings_dir=recordings_dir,
        terminal_dir=terminal_dir,
        tui_captures_dir=tui_captures_dir,
    )

    # Populate render_staging_path when video is enabled
    if video_enabled:
        # Path is relative to evidence_root for consistency with evidence items
        render_staging_rel = f"{vm_name}/{job_id}/render_staging"
        manifest.render_staging_path = render_staging_rel

        # Render the video and populate video_output_path
        staging_dir = evidence_root / render_staging_rel

        # Save manifest to temp file for render.ts CLI
        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False, encoding="utf-8") as tmp_manifest:
            tmp_manifest.write(manifest.model_dump_json(indent=2))
            tmp_manifest_path = Path(tmp_manifest.name)

        try:
            mp4_path = render_video(
                manifest_path=tmp_manifest_path, output_dir=staging_dir, repo_root=evidence_root.parent.parent
            )
            manifest.video_output_path = str(mp4_path)
        except RuntimeError as e:
            msg = f"Video rendering failed for job {job_id}: {e}"
            raise ValueError(msg) from e
        finally:
            # Clean up temp manifest file
            tmp_manifest_path.unlink(missing_ok=True)  # noqa: ASYNC240

    # Save if path provided
    if save_path is not None:
        save_manifest(manifest, save_path)

    return manifest
