# grantflow/tests/test_integration_exports.py

import io

from docx import Document
from fastapi.testclient import TestClient

import grantflow.api.routes.exports as exports_routes
from grantflow.api.app import app
from grantflow.core.config import config

client = TestClient(app)


def test_export_endpoint_blocks_when_strict_grounding_gate_failed_unless_overridden():
    payload = {
        "state": {
            "donor_id": "usaid",
            "toc_draft": {"toc": {"brief": "Sample ToC"}},
            "logframe_draft": {"indicators": []},
            "grounding_gate": {
                "mode": "strict",
                "passed": False,
                "blocking": True,
                "summary": "fallback_or_low_rag_citations_dominate",
                "reasons": ["fallback_or_low_rag_citations_dominate"],
            },
        }
    }

    blocked = client.post("/export", json={"payload": payload, "format": "docx"})
    assert blocked.status_code == 409
    detail = blocked.json()["detail"]
    assert detail["reason"] == "grounding_gate_strict_block"

    allowed = client.post("/export", json={"payload": payload, "format": "docx", "allow_unsafe_export": True})
    assert allowed.status_code == 200
    assert allowed.headers["content-type"].startswith(
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
    )


def test_export_endpoint_docx_defaults_to_client_ready_without_diagnostics_sections():
    payload = {
        "state": {
            "donor_id": "usaid",
            "toc_draft": {"toc": {"project_goal": "Improve service quality."}},
            "logframe_draft": {"indicators": []},
            "citations": [
                {
                    "source": "USAID ADS 201 p.12",
                    "excerpt": "Official indicator guidance excerpt",
                    "confidence": 0.82,
                    "stage": "architect",
                }
            ],
            "critic_findings": [{"title": "ToC Schema Invalid", "status": "open", "severity": "high"}],
            "review_comments": [{"message": "Adjusted objective wording.", "status": "resolved"}],
            "quality_summary": {"quality_score": 8.5, "fatal_flaw_count": 1},
        }
    }

    default_resp = client.post("/export", json={"payload": payload, "format": "docx"})
    assert default_resp.status_code == 200
    default_text = "\n".join(p.text for p in Document(io.BytesIO(default_resp.content)).paragraphs)
    assert "USAID Results Framework" in default_text
    assert "Citation Traceability" not in default_text
    assert "Critic Findings" not in default_text
    assert "Review Comments" not in default_text
    assert "Quality Summary" not in default_text
    assert "Template Profile" not in default_text
    assert "Export Contract Check" not in default_text

    diagnostics_resp = client.post(
        "/export",
        json={"payload": payload, "format": "docx", "include_diagnostics": True},
    )
    assert diagnostics_resp.status_code == 200
    diagnostics_text = "\n".join(p.text for p in Document(io.BytesIO(diagnostics_resp.content)).paragraphs)
    assert "Citation Traceability" in diagnostics_text
    assert "Review Comments" in diagnostics_text
    assert "Template Profile" in diagnostics_text
    assert "Export Contract Check" in diagnostics_text


def test_export_endpoint_blocks_when_runtime_grounded_gate_policy_requires_pass(monkeypatch):
    monkeypatch.setattr(config.graph, "export_require_grounded_gate_pass", True)
    payload = {
        "state": {
            "donor_id": "usaid",
            "toc_draft": {"toc": {"brief": "Sample ToC"}},
            "logframe_draft": {"indicators": []},
            "grounded_quality_gate": {
                "mode": "strict",
                "passed": False,
                "blocking": True,
                "summary": "runtime_non_retrieval_signals_high",
                "reasons": ["non_retrieval_rate_above_max"],
                "failed_sections": ["toc"],
            },
        }
    }

    blocked = client.post("/export", json={"payload": payload, "format": "docx"})
    assert blocked.status_code == 409
    detail = blocked.json()["detail"]
    assert detail["reason"] == "runtime_grounded_quality_gate_block"
    grounded_gate = detail["grounded_gate"]
    assert grounded_gate["blocking"] is True
    assert grounded_gate["passed"] is False

    allowed = client.post("/export", json={"payload": payload, "format": "docx", "allow_unsafe_export": True})
    assert allowed.status_code == 200
    assert allowed.headers["content-type"].startswith(
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
    )


def test_export_endpoint_blocks_production_export_when_contract_policy_strict_and_missing_sections(monkeypatch):
    monkeypatch.setattr(config.graph, "export_contract_policy_mode", "strict")
    payload = {
        "state": {
            "donor_id": "eu",
            "toc_draft": {
                "toc": {
                    "overall_objective": {
                        "objective_id": "OO1",
                        "title": "Digital governance",
                        "rationale": "EU fit",
                    }
                }
            },
            "logframe_draft": {"indicators": []},
        }
    }

    blocked = client.post("/export", json={"payload": payload, "format": "docx", "production_export": True})
    assert blocked.status_code == 409
    detail = blocked.json()["detail"]
    assert detail["reason"] == "export_contract_policy_block"
    gate = detail["export_contract_gate"]
    assert gate["mode"] == "strict"
    assert gate["blocking"] is True
    assert gate["status"] == "warning"
    assert "specific_objectives" in (gate.get("missing_required_sections") or [])
    assert "missing_required_toc_sections" in (gate.get("reasons") or [])

    non_production = client.post("/export", json={"payload": payload, "format": "docx"})
    assert non_production.status_code == 200
    assert non_production.headers["content-type"].startswith(
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
    )
    assert non_production.headers.get("x-grantflow-export-contract-mode") == "strict"
    assert non_production.headers.get("x-grantflow-export-contract-status") == "warning"

    overridden = client.post(
        "/export",
        json={"payload": payload, "format": "docx", "production_export": True, "allow_unsafe_export": True},
    )
    assert overridden.status_code == 200
    assert overridden.headers["content-type"].startswith(
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
    )


def test_export_endpoint_blocks_production_xlsx_when_primary_headers_missing(monkeypatch):
    from openpyxl import Workbook

    monkeypatch.setattr(config.graph, "export_contract_policy_mode", "strict")

    def _stub_build_xlsx_from_logframe(*_args, **_kwargs):
        wb = Workbook()
        ws = wb.active
        ws.title = "LogFrame"
        ws.append(["Indicator ID", "Name", "Justification", "Citation", "Baseline", "Target"])
        usaid_sheet = wb.create_sheet("USAID_RF")
        usaid_sheet.append(["DO ID", "DO Description"])
        wb.create_sheet("Template Meta")
        bio = io.BytesIO()
        wb.save(bio)
        bio.seek(0)
        return bio.read()

    monkeypatch.setattr(exports_routes, "build_xlsx_from_logframe", _stub_build_xlsx_from_logframe)

    payload = {
        "state": {
            "donor_id": "usaid",
            "toc_draft": {
                "toc": {
                    "project_goal": "Improve civic services",
                    "development_objectives": [
                        {"do_id": "DO1", "description": "Improved digital delivery", "intermediate_results": []}
                    ],
                }
            },
            "logframe_draft": {"indicators": []},
        }
    }

    blocked = client.post("/export", json={"payload": payload, "format": "xlsx", "production_export": True})
    assert blocked.status_code == 409
    detail = blocked.json()["detail"]
    assert detail["reason"] == "export_contract_policy_block"
    gate = detail["export_contract_gate"]
    assert gate["mode"] == "strict"
    assert gate["blocking"] is True
    assert gate["status"] == "warning"
    assert "missing_required_primary_sheet_headers" in (gate.get("reasons") or [])
    assert "IR ID" in (gate.get("missing_required_primary_sheet_headers") or [])

    non_production = client.post("/export", json={"payload": payload, "format": "xlsx"})
    assert non_production.status_code == 200
    assert non_production.headers["content-type"].startswith(
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
    )
    assert non_production.headers.get("x-grantflow-export-contract-status") == "warning"


def test_export_endpoint_blocks_when_export_claim_support_policy_strict_and_below_threshold(monkeypatch):
    monkeypatch.setattr(config.graph, "export_grounding_policy_mode", "strict")
    monkeypatch.setattr(config.graph, "export_grounding_min_architect_citations", 3)
    monkeypatch.setattr(config.graph, "export_grounding_min_claim_support_rate", 0.75)

    payload = {
        "state": {
            "donor_id": "usaid",
            "toc_draft": {"toc": {"brief": "Sample ToC"}},
            "logframe_draft": {"indicators": []},
            "citations": [
                {"stage": "architect", "citation_type": "rag_claim_support", "citation_confidence": 0.9},
                {"stage": "architect", "citation_type": "fallback_namespace", "citation_confidence": 0.1},
                {"stage": "architect", "citation_type": "rag_low_confidence", "citation_confidence": 0.2},
            ],
        }
    }

    blocked = client.post("/export", json={"payload": payload, "format": "docx"})
    assert blocked.status_code == 409
    detail = blocked.json()["detail"]
    assert detail["reason"] == "export_grounding_policy_block"
    policy = detail["export_grounding_policy"]
    assert policy["mode"] == "strict"
    assert policy["blocking"] is True
    assert policy["architect_citation_count"] == 3
    assert policy["architect_claim_support_citation_count"] == 1
    assert policy["architect_claim_support_rate"] == 0.3333
    assert "claim_support_rate_below_min" in (policy.get("reasons") or [])

    allowed = client.post("/export", json={"payload": payload, "format": "docx", "allow_unsafe_export": True})
    assert allowed.status_code == 200
    assert allowed.headers["content-type"].startswith(
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
    )


def test_export_endpoint_blocks_when_export_traceability_policy_strict_and_below_threshold(monkeypatch):
    monkeypatch.setattr(config.graph, "export_grounding_policy_mode", "strict")
    monkeypatch.setattr(config.graph, "export_grounding_min_architect_citations", 1)
    monkeypatch.setattr(config.graph, "export_grounding_min_claim_support_rate", 0.0)
    monkeypatch.setattr(config.graph, "export_grounding_min_traceability_complete_rate", 1.0)
    monkeypatch.setattr(config.graph, "export_grounding_max_traceability_gap_rate", 0.0)

    payload = {
        "state": {
            "donor_id": "usaid",
            "toc_draft": {"toc": {"brief": "Sample ToC"}},
            "logframe_draft": {"indicators": []},
            "citations": [
                {"stage": "architect", "citation_type": "rag_claim_support", "citation_confidence": 0.95},
                {"stage": "architect", "citation_type": "rag_claim_support", "citation_confidence": 0.91},
            ],
        }
    }

    blocked = client.post("/export", json={"payload": payload, "format": "docx"})
    assert blocked.status_code == 409
    detail = blocked.json()["detail"]
    assert detail["reason"] == "export_grounding_policy_block"
    policy = detail["export_grounding_policy"]
    assert policy["mode"] == "strict"
    assert policy["blocking"] is True
    assert policy["architect_traceability_complete_rate"] == 0.0
    assert policy["architect_traceability_gap_rate"] == 1.0
    assert "traceability_complete_rate_below_min" in (policy.get("reasons") or [])
    assert "traceability_gap_rate_above_max" in (policy.get("reasons") or [])

    allowed = client.post("/export", json={"payload": payload, "format": "docx", "allow_unsafe_export": True})
    assert allowed.status_code == 200
    assert allowed.headers["content-type"].startswith(
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
    )


def test_export_endpoint_warn_mode_does_not_block_on_low_claim_support(monkeypatch):
    monkeypatch.setattr(config.graph, "export_grounding_policy_mode", "warn")
    monkeypatch.setattr(config.graph, "export_grounding_min_architect_citations", 3)
    monkeypatch.setattr(config.graph, "export_grounding_min_claim_support_rate", 0.75)

    payload = {
        "state": {
            "donor_id": "usaid",
            "toc_draft": {"toc": {"brief": "Sample ToC"}},
            "logframe_draft": {"indicators": []},
            "citations": [
                {"stage": "architect", "citation_type": "fallback_namespace", "citation_confidence": 0.1},
                {"stage": "architect", "citation_type": "rag_low_confidence", "citation_confidence": 0.2},
                {"stage": "architect", "citation_type": "rag_low_confidence", "citation_confidence": 0.22},
            ],
        }
    }

    export = client.post("/export", json={"payload": payload, "format": "docx"})
    assert export.status_code == 200
    assert export.headers["content-type"].startswith(
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
    )
