"""End-to-end integration tests for the full ``orxaq run`` pipeline.

Verifies complete filing directory structure, SHA-256 manifest integrity,
JSON schema compliance, and CLI round-trip.
"""

from __future__ import annotations

import hashlib
import json
import subprocess
import sys
from pathlib import Path

import pytest

from orxaq.credit.filing import FILING_OUTPUT_FILES
from orxaq.orchestration.run import RunConfig, run_pipeline

# ── Shared fixture ───────────────────────────────────────────────────────────


@pytest.fixture()
def e2e_result(tmp_path: Path):
    """Run the full pipeline once with n_loans=50 for speed."""
    cfg = RunConfig(
        institution_id="E2E_BANK",
        period="2026Q1",
        output_dir=str(tmp_path / "filing"),
        n_loans=50,
        seed=42,
    )
    return run_pipeline(cfg)


# ── File structure ───────────────────────────────────────────────────────────


def test_full_run_pipeline_creates_all_files(e2e_result) -> None:
    out = Path(e2e_result.output_dir)
    missing = [f for f in FILING_OUTPUT_FILES if not (out / f).exists()]
    assert missing == [], f"Missing files: {missing}"


def test_manifest_exists(e2e_result) -> None:
    assert (Path(e2e_result.output_dir) / "manifest.json").exists()


# ── Manifest integrity ───────────────────────────────────────────────────────


def test_manifest_checksums_match_files(e2e_result) -> None:
    out = Path(e2e_result.output_dir)
    manifest_path = out / "manifest.json"
    manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
    files_in_manifest = manifest.get("files", {})
    assert len(files_in_manifest) > 0, "manifest has no files section"
    for fname, info in files_in_manifest.items():
        fpath = out / fname
        assert fpath.exists(), f"{fname} listed in manifest but not on disk"
        actual = hashlib.sha256(fpath.read_bytes()).hexdigest()
        expected = info["sha256"]
        assert actual == expected, f"SHA-256 mismatch for {fname}"


# ── JSON schema ──────────────────────────────────────────────────────────────


def test_cecl_disclosure_is_valid_json(e2e_result) -> None:
    path = Path(e2e_result.output_dir) / "cecl_disclosure.json"
    data = json.loads(path.read_text(encoding="utf-8"))
    assert isinstance(data, dict)


def test_cecl_disclosure_has_framework_cecl(e2e_result) -> None:
    path = Path(e2e_result.output_dir) / "cecl_disclosure.json"
    data = json.loads(path.read_text(encoding="utf-8"))
    framework = data.get("framework", "")
    assert "CECL" in framework.upper()


def test_ccar_export_is_valid_json(e2e_result) -> None:
    path = Path(e2e_result.output_dir) / "ccar_export.json"
    data = json.loads(path.read_text(encoding="utf-8"))
    assert isinstance(data, dict)


def test_ccar_export_has_framework_ccar(e2e_result) -> None:
    path = Path(e2e_result.output_dir) / "ccar_export.json"
    data = json.loads(path.read_text(encoding="utf-8"))
    framework = str(data.get("framework", ""))
    assert "CCAR" in framework.upper()


# ── RunResult metrics ────────────────────────────────────────────────────────


def test_run_result_ecl_total_positive(e2e_result) -> None:
    assert e2e_result.ecl_total > 0.0


def test_run_result_duration_positive(e2e_result) -> None:
    assert e2e_result.duration_s > 0.0


def test_run_result_audit_entries_positive(e2e_result) -> None:
    assert e2e_result.audit_entries > 0


def test_run_result_institution_id_matches_config(e2e_result) -> None:
    assert e2e_result.institution_id == "E2E_BANK"


def test_run_result_period_matches_config(e2e_result) -> None:
    assert e2e_result.period == "2026Q1"


# ── Cross-institution reproducibility ────────────────────────────────────────


def test_different_institutions_produce_same_ecl_given_same_seed(
    tmp_path: Path,
) -> None:
    """Same seed → same synthetic data → same ECL regardless of institution_id."""
    r_a = run_pipeline(
        RunConfig(
            institution_id="BANK_A",
            output_dir=str(tmp_path / "a"),
            n_loans=30,
            seed=77,
        )
    )
    r_b = run_pipeline(
        RunConfig(
            institution_id="BANK_B",
            output_dir=str(tmp_path / "b"),
            n_loans=30,
            seed=77,
        )
    )
    assert round(r_a.ecl_total, 4) == round(r_b.ecl_total, 4)


# ── CLI subprocess round-trip ────────────────────────────────────────────────


def _orxaq_cmd() -> list[str]:
    """Return argv prefix for invoking the orxaq CLI from this source tree."""
    return [
        sys.executable,
        "-c",
        "from orxaq.experiences.cli import main; import sys; sys.exit(main())",
    ]


def _subprocess_env() -> dict[str, str]:
    """Subprocess env that puts the worktree src on PYTHONPATH."""
    import os

    env = os.environ.copy()
    # Ensure the worktree's src is first so our modified cli.py is used
    wt_src = str(Path(__file__).parent.parent / "src")
    existing = env.get("PYTHONPATH", "")
    env["PYTHONPATH"] = f"{wt_src}:{existing}" if existing else wt_src
    return env


def test_cli_run_end_to_end_exits_zero(tmp_path: Path) -> None:
    prefix = _orxaq_cmd()
    result = subprocess.run(
        [
            *prefix,
            "run",
            "--institution",
            "CLI_E2E",
            "--output",
            str(tmp_path / "cli_out"),
            "--loans",
            "30",
        ],
        capture_output=True,
        text=True,
        timeout=120,
        env=_subprocess_env(),
    )
    assert result.returncode == 0, result.stderr


def test_cli_run_json_output_valid_structure(tmp_path: Path) -> None:
    prefix = _orxaq_cmd()
    result = subprocess.run(
        [
            *prefix,
            "--json",
            "run",
            "--institution",
            "CLI_JSON",
            "--output",
            str(tmp_path / "cli_json_out"),
            "--loans",
            "30",
        ],
        capture_output=True,
        text=True,
        timeout=120,
        env=_subprocess_env(),
    )
    assert result.returncode == 0, result.stderr
    data = json.loads(result.stdout)
    assert "institution_id" in data
    assert "ecl_total" in data
    assert "files" in data
