"""Extended integration tests for FilingGenerator — 15 real-pipeline tests.

All tests use a module-scoped fixture that runs the full pipeline once
so the suite executes in < 2 seconds after the first run.
"""

from __future__ import annotations

import csv
import hashlib
import json
from pathlib import Path

import pytest

from orxaq.credit.filing import FILING_OUTPUT_FILES, FilingConfig, FilingGenerator
from orxaq.credit.regulatory_export import validate_ccar_schema, validate_cecl_schema

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


@pytest.fixture(scope="module")
def filing_result(tmp_path_factory):
    """Run the full FilingGenerator pipeline once per module."""
    out = tmp_path_factory.mktemp("e2e_filing")
    cfg = FilingConfig(
        institution_id="E2E_BANK",
        period="2026Q1",
        output_dir=str(out),
        seed=7,
        n_samples=80,
        horizon_quarters=9,
    )
    return FilingGenerator(cfg).generate()


@pytest.fixture(scope="module")
def out_dir(filing_result) -> Path:
    return Path(filing_result.output_dir)


# ── Tests ──────────────────────────────────────────────────────


def test_duration_positive(filing_result) -> None:
    assert filing_result.duration_seconds > 0


def test_files_list_length(filing_result) -> None:
    assert len(filing_result.files) == len(FILING_OUTPUT_FILES)


def test_all_output_files_exist(out_dir) -> None:
    for fname in FILING_OUTPUT_FILES:
        assert (out_dir / fname).exists(), f"Missing: {fname}"


def test_scenario_comparison_has_three_scenarios(out_dir) -> None:
    """scenario_comparison.csv has 9 quarters × 3 scenarios = 27 rows."""
    with (out_dir / "scenario_comparison.csv").open() as f:
        rows = list(csv.DictReader(f))
    scenarios = {r["scenario"] for r in rows}
    assert scenarios == {"baseline", "adverse", "severely_adverse"}, scenarios


def test_schedule_h1_has_nine_quarters(out_dir) -> None:
    with (out_dir / "schedule_h1.csv").open() as f:
        rows = list(csv.DictReader(f))
    assert len(rows) == 9, f"Expected 9 quarter rows, got {len(rows)}"


def test_html_report_contains_doctype(out_dir) -> None:
    html = (out_dir / "regulatory_report.html").read_text()
    assert "<!DOCTYPE" in html or "<!doctype" in html.lower()


def test_html_report_contains_cecl_and_ccar(out_dir) -> None:
    html = (out_dir / "regulatory_report.html").read_text()
    assert "CECL" in html
    assert "CCAR" in html


def test_audit_trail_has_five_plus_entries(out_dir) -> None:
    audit = json.loads((out_dir / "audit_trail.json").read_text())
    assert len(audit) >= 5
    for entry in audit:
        assert "action" in entry, f"Entry missing 'action': {entry}"
        assert "timestamp" in entry, f"Entry missing 'timestamp': {entry}"


def test_manifest_sha256_match_files(out_dir) -> None:
    manifest = json.loads((out_dir / "manifest.json").read_text())
    files_dict = manifest.get("files", {})
    checked = 0
    for fname, meta in files_dict.items():
        fpath = out_dir / fname
        if fpath.exists() and isinstance(meta, dict) and "sha256" in meta:
            actual = hashlib.sha256(fpath.read_bytes()).hexdigest()
            assert actual == meta["sha256"], f"SHA-256 mismatch for {fname}"
            checked += 1
    assert checked > 0, "No checksum entries verified in manifest"


def test_non_timestamp_files_deterministic(tmp_path) -> None:
    """schedule_h1.csv and scenario_comparison.csv must be identical across runs."""

    def _run(out):
        cfg = FilingConfig(
            institution_id="DET_E2E",
            period="2026Q1",
            output_dir=str(out),
            seed=55,
            n_samples=60,
            horizon_quarters=9,
        )
        return Path(FilingGenerator(cfg).generate().output_dir)

    d1 = _run(tmp_path / "r1")
    d2 = _run(tmp_path / "r2")
    for fname in ("schedule_h1.csv", "scenario_comparison.csv"):
        h1 = hashlib.sha256((d1 / fname).read_bytes()).hexdigest()
        h2 = hashlib.sha256((d2 / fname).read_bytes()).hexdigest()
        assert h1 == h2, f"{fname} not deterministic"


def test_custom_horizon_quarters_produces_valid_output(tmp_path) -> None:
    cfg = FilingConfig(
        institution_id="HORIZON_BANK",
        period="2026Q1",
        output_dir=str(tmp_path),
        seed=1,
        n_samples=50,
        horizon_quarters=4,
    )
    result = FilingGenerator(cfg).generate()
    assert len(result.files) == len(FILING_OUTPUT_FILES)
    assert result.duration_seconds > 0


def test_cecl_disclosure_passes_schema(out_dir) -> None:
    cecl = json.loads((out_dir / "cecl_disclosure.json").read_text())
    errors = validate_cecl_schema(cecl)
    assert errors == [], f"CECL schema errors: {errors}"


def test_ccar_export_passes_schema(out_dir) -> None:
    ccar = json.loads((out_dir / "ccar_export.json").read_text())
    errors = validate_ccar_schema(ccar)
    assert errors == [], f"CCAR schema errors: {errors}"


def test_institution_id_in_cecl_and_ccar(out_dir) -> None:
    cecl = json.loads((out_dir / "cecl_disclosure.json").read_text())
    ccar = json.loads((out_dir / "ccar_export.json").read_text())
    cecl_str = json.dumps(cecl)
    ccar_str = json.dumps(ccar)
    assert "E2E_BANK" in cecl_str, "institution_id missing from CECL disclosure"
    assert "E2E_BANK" in ccar_str, "institution_id missing from CCAR export"


def test_markdown_report_contains_cecl_and_ccar(out_dir) -> None:
    md = (out_dir / "regulatory_report.md").read_text()
    assert "CECL" in md
    assert "CCAR" in md
