"""Tests for orxaq filing validate CLI subcommand."""

from __future__ import annotations

import hashlib
import json
from pathlib import Path
from typing import Any

import pytest

from orxaq.experiences.cli import main

# ── Helpers ──────────────────────────────────────────────────


def _sha256(path: Path) -> str:
    h = hashlib.sha256()
    with open(path, "rb") as fh:
        for chunk in iter(lambda: fh.read(65536), b""):
            h.update(chunk)
    return h.hexdigest()


def _write_valid_package(out: Path) -> None:
    """Write a minimal but valid filing package to *out*."""
    cecl: dict[str, Any] = {
        "institution_id": "BANK001",
        "framework": "ASC 326",
        "reporting_period": "2026Q1",
        "asc_326_disclosure": {"methodology": "PD/LGD"},
        "methodology_attestation": "Approved",
        "quantitative_results": {"allowance": 1_234_567.89},
        "model_governance": {"reviewed": True},
    }
    ccar: dict[str, Any] = {
        "institution_id": "BANK001",
        "framework": "FR Y-14A",
        "reporting_period": "2026Q1",
        "fr_y14a_fields": {"scenario": "adverse"},
        "scenario_results": {"adverse": {"ecl": 100.0}},
        "call_report_rc_r": {"capital_ratio": 0.115},
        "model_governance": {"reviewed": True},
    }
    schedule_h1 = "quarter,ecl\n2026Q1,100.0\n2026Q2,110.0\n"
    scenario_csv = "scenario,ecl\nadverse,100.0\nseverely_adverse,150.0\n"
    html_content = "<html><body><h1>Regulatory Report</h1></body></html>"
    md_content = "# Regulatory Report\n\nPeriod: 2026Q1\n"
    audit: list[dict[str, Any]] = [{"event": "filing_started", "ts": "2026-01-01T00:00:00Z"}]

    files = {
        "cecl_disclosure.json": json.dumps(cecl),
        "ccar_export.json": json.dumps(ccar),
        "schedule_h1.csv": schedule_h1,
        "scenario_comparison.csv": scenario_csv,
        "regulatory_report.html": html_content,
        "regulatory_report.md": md_content,
        "audit_trail.json": json.dumps(audit),
    }
    for fname, content in files.items():
        (out / fname).write_text(content, encoding="utf-8")

    # Build manifest with correct SHA-256
    manifest_files: dict[str, Any] = {}
    for fname in files:
        p = out / fname
        manifest_files[fname] = {"sha256": _sha256(p), "size": p.stat().st_size}
    manifest: dict[str, Any] = {
        "period": "2026Q1",
        "institution_id": "BANK001",
        "files": manifest_files,
    }
    (out / "manifest.json").write_text(json.dumps(manifest), encoding="utf-8")


@pytest.fixture
def valid_pkg(tmp_path: Path) -> Path:
    _write_valid_package(tmp_path)
    return tmp_path


@pytest.fixture
def empty_dir(tmp_path: Path) -> Path:
    return tmp_path


# ── Tests: valid package ──────────────────────────────────────


class TestFilingValidatePass:
    def test_rc_zero(self, valid_pkg: Path) -> None:
        rc = main(["filing", "validate", str(valid_pkg)])
        assert rc == 0

    def test_stdout_pass(self, valid_pkg: Path, capsys: pytest.CaptureFixture) -> None:
        main(["filing", "validate", str(valid_pkg)])
        out = capsys.readouterr().out
        assert "PASS" in out

    def test_stdout_lists_checks(self, valid_pkg: Path, capsys: pytest.CaptureFixture) -> None:
        main(["filing", "validate", str(valid_pkg)])
        out = capsys.readouterr().out
        assert "files_present" in out
        assert "sha256_checksums" in out

    def test_json_status_pass(self, valid_pkg: Path, capsys: pytest.CaptureFixture) -> None:
        main(["--json", "filing", "validate", str(valid_pkg)])
        out = capsys.readouterr().out
        data = json.loads(out)
        assert data["status"] == "pass"

    def test_json_has_checks(self, valid_pkg: Path, capsys: pytest.CaptureFixture) -> None:
        main(["--json", "filing", "validate", str(valid_pkg)])
        data = json.loads(capsys.readouterr().out)
        assert isinstance(data["checks"], list)
        assert len(data["checks"]) >= 3

    def test_json_files_verified(self, valid_pkg: Path, capsys: pytest.CaptureFixture) -> None:
        main(["--json", "filing", "validate", str(valid_pkg)])
        data = json.loads(capsys.readouterr().out)
        assert data["files_verified"] > 0

    def test_json_generated_at(self, valid_pkg: Path, capsys: pytest.CaptureFixture) -> None:
        main(["--json", "filing", "validate", str(valid_pkg)])
        data = json.loads(capsys.readouterr().out)
        assert "generated_at" in data
        assert "T" in data["generated_at"]

    def test_cecl_schema_check_present(
        self, valid_pkg: Path, capsys: pytest.CaptureFixture
    ) -> None:
        main(["--json", "filing", "validate", str(valid_pkg)])
        data = json.loads(capsys.readouterr().out)
        names = [c["name"] for c in data["checks"]]
        assert "cecl_schema" in names

    def test_ccar_schema_check_present(
        self, valid_pkg: Path, capsys: pytest.CaptureFixture
    ) -> None:
        main(["--json", "filing", "validate", str(valid_pkg)])
        data = json.loads(capsys.readouterr().out)
        names = [c["name"] for c in data["checks"]]
        assert "ccar_schema" in names


# ── Tests: missing files ──────────────────────────────────────


class TestFilingValidateMissingFiles:
    def test_rc_one_empty_dir(self, empty_dir: Path) -> None:
        rc = main(["filing", "validate", str(empty_dir)])
        assert rc == 1

    def test_json_status_fail_empty(self, empty_dir: Path, capsys: pytest.CaptureFixture) -> None:
        main(["--json", "filing", "validate", str(empty_dir)])
        data = json.loads(capsys.readouterr().out)
        assert data["status"] == "fail"

    def test_files_present_check_fails(
        self, empty_dir: Path, capsys: pytest.CaptureFixture
    ) -> None:
        main(["--json", "filing", "validate", str(empty_dir)])
        data = json.loads(capsys.readouterr().out)
        fp_check = next(c for c in data["checks"] if c["name"] == "files_present")
        assert not fp_check["passed"]

    def test_missing_detail_lists_files(
        self, empty_dir: Path, capsys: pytest.CaptureFixture
    ) -> None:
        main(["--json", "filing", "validate", str(empty_dir)])
        data = json.loads(capsys.readouterr().out)
        fp_check = next(c for c in data["checks"] if c["name"] == "files_present")
        assert "Missing" in fp_check["detail"]

    def test_partial_package_fails(self, tmp_path: Path, capsys: pytest.CaptureFixture) -> None:
        # Write only cecl and ccar, leave rest missing
        (tmp_path / "cecl_disclosure.json").write_text('{"a":1}')
        (tmp_path / "ccar_export.json").write_text('{"b":2}')
        main(["--json", "filing", "validate", str(tmp_path)])
        data = json.loads(capsys.readouterr().out)
        assert data["status"] == "fail"


# ── Tests: tampered checksums ─────────────────────────────────


class TestFilingValidateTampered:
    def test_tampered_file_fails(self, valid_pkg: Path, capsys: pytest.CaptureFixture) -> None:
        # Corrupt the cecl file after recording its checksum in the manifest
        (valid_pkg / "cecl_disclosure.json").write_text('{"tampered": true}')
        main(["--json", "filing", "validate", str(valid_pkg)])
        data = json.loads(capsys.readouterr().out)
        cs_check = next(c for c in data["checks"] if c["name"] == "sha256_checksums")
        assert not cs_check["passed"]

    def test_cli_returns_one(self, valid_pkg: Path) -> None:
        # Corrupt the audit trail file
        (valid_pkg / "audit_trail.json").write_text('{"tampered": true}')
        rc = main(["filing", "validate", str(valid_pkg)])
        assert rc == 1

    def test_checksum_detail_has_mismatch(
        self, valid_pkg: Path, capsys: pytest.CaptureFixture
    ) -> None:
        (valid_pkg / "ccar_export.json").write_text('{"hacked": true}')
        main(["--json", "filing", "validate", str(valid_pkg)])
        data = json.loads(capsys.readouterr().out)
        cs_check = next(c for c in data["checks"] if c["name"] == "sha256_checksums")
        assert "mismatch" in cs_check["detail"].lower()


# ── Tests: invalid JSON ───────────────────────────────────────


class TestFilingValidateBadJson:
    def test_bad_manifest_json(self, tmp_path: Path, capsys: pytest.CaptureFixture) -> None:
        _write_valid_package(tmp_path)
        (tmp_path / "manifest.json").write_text("NOT JSON")
        main(["--json", "filing", "validate", str(tmp_path)])
        data = json.loads(capsys.readouterr().out)
        mp_check = next(c for c in data["checks"] if c["name"] == "manifest_parseable")
        assert not mp_check["passed"]

    def test_bad_cecl_json(self, tmp_path: Path, capsys: pytest.CaptureFixture) -> None:
        _write_valid_package(tmp_path)
        (tmp_path / "cecl_disclosure.json").write_text("INVALID JSON {")
        main(["--json", "filing", "validate", str(tmp_path)])
        data = json.loads(capsys.readouterr().out)
        cecl_check = next(c for c in data["checks"] if c["name"] == "cecl_schema")
        assert not cecl_check["passed"]


# ── Tests: structured output ──────────────────────────────────


class TestFilingValidateStructuredOutput:
    def test_output_dir_in_text(self, valid_pkg: Path, capsys: pytest.CaptureFixture) -> None:
        main(["filing", "validate", str(valid_pkg)])
        out = capsys.readouterr().out
        assert str(valid_pkg) in out

    def test_validate_help_exits(self) -> None:
        with pytest.raises(SystemExit) as exc:
            main(["filing", "validate", "--help"])
        assert exc.value.code == 0

    def test_default_dir_used_without_arg(self, monkeypatch: pytest.MonkeyPatch) -> None:
        """When no positional arg, output_dir defaults to '.'."""
        called_with: list[str] = []

        def fake_validate(output_dir: str, fmt: str = "text"):  # type: ignore[return]
            called_with.append(output_dir)
            return {"status": "pass", "checks": [], "files_verified": 0, "generated_at": ""}

        monkeypatch.setattr(
            "orxaq.experiences.cli._cmd_filing_validate",
            lambda args, engine: (
                called_with.append(getattr(args, "output_dir", ".")),
                0,
            )[-1],
        )
        main(["filing", "validate"])
        assert called_with[0] == "."


# ── Targeted coverage: lines 86-88, 151-152 ──────────────────


class TestFilingValidateMissingManifestEntry:
    """Cover lines 86-88: manifest entry references a deleted file."""

    def test_missing_file_in_manifest(self, tmp_path: Path) -> None:
        from orxaq.credit.filing import FilingConfig, FilingGenerator
        from orxaq.credit.filing_validate import validate_filing_package

        cfg = FilingConfig(
            institution_id="COV001",
            period="2026Q1",
            output_dir=str(tmp_path),
            seed=11,
            n_samples=30,
            horizon_quarters=4,
        )
        FilingGenerator(cfg).generate()
        # Deleted a file that IS listed in the manifest so checksum loop hits "file missing"
        target = tmp_path / "schedule_h1.csv"
        if target.exists():
            target.unlink()
        # Make sure manifest still lists the file
        import json

        manifest_path = tmp_path / "manifest.json"
        manifest = json.loads(manifest_path.read_text())
        if "schedule_h1.csv" not in manifest.get("files", {}):
            manifest.setdefault("files", {})["schedule_h1.csv"] = {"sha256": "deadbeef"}
            manifest_path.write_text(json.dumps(manifest))
        result = validate_filing_package(str(tmp_path))
        checksum_chk = next(c for c in result["checks"] if c["name"] == "sha256_checksums")
        assert not checksum_chk["passed"]
        assert "file missing" in checksum_chk["detail"] or "Missing" in checksum_chk["detail"]


class TestFilingValidateCCARJsonError:
    """Cover lines 151-152: ccar_export.json contains invalid JSON."""

    def test_ccar_json_decode_error(self, tmp_path: Path) -> None:
        from orxaq.credit.filing import FilingConfig, FilingGenerator
        from orxaq.credit.filing_validate import validate_filing_package

        cfg = FilingConfig(
            institution_id="COV002",
            period="2026Q1",
            output_dir=str(tmp_path),
            seed=22,
            n_samples=30,
            horizon_quarters=4,
        )
        FilingGenerator(cfg).generate()
        (tmp_path / "ccar_export.json").write_text("{invalid json}")
        result = validate_filing_package(str(tmp_path))
        ccar_chk = next(c for c in result["checks"] if c["name"] == "ccar_schema")
        assert not ccar_chk["passed"]
        assert "JSON error" in ccar_chk["detail"]
