# grantflow/tests/test_integration_quality_summary.py

from fastapi.testclient import TestClient


from grantflow.api import runtime_state

from grantflow.api.app import app

client = TestClient(app)


def test_quality_summary_endpoint_aggregates_quality_signals():
    job_id = "quality-job-1"
    runtime_state.INGEST_AUDIT_STORE.clear()
    runtime_state.INGEST_AUDIT_STORE.append(
        {
            "event_id": "ing-q-1",
            "ts": "2026-02-24T09:55:00+00:00",
            "donor_id": "usaid",
            "namespace": "usaid_ads201",
            "filename": "ads-guidance.pdf",
            "content_type": "application/pdf",
            "metadata": {"doc_family": "donor_policy", "source_type": "donor_guidance"},
            "result": {"chunks_ingested": 10},
        }
    )
    runtime_state.INGEST_AUDIT_STORE.append(
        {
            "event_id": "ing-q-2",
            "ts": "2026-02-24T09:56:00+00:00",
            "donor_id": "usaid",
            "namespace": "usaid_ads201",
            "filename": "kz-context.pdf",
            "content_type": "application/pdf",
            "metadata": {"doc_family": "country_context", "source_type": "country_context"},
            "result": {"chunks_ingested": 8},
        }
    )
    runtime_state.JOB_STORE.set(
        job_id,
        {
            "status": "done",
            "strict_preflight": True,
            "generate_preflight": {
                "donor_id": "usaid",
                "risk_level": "medium",
                "go_ahead": True,
                "warning_count": 1,
                "warnings": [{"code": "LOW_DOC_COVERAGE", "severity": "medium"}],
            },
            "client_metadata": {
                "demo_generate_preset_key": "usaid_gov_ai_kazakhstan",
                "rag_readiness": {
                    "expected_doc_families": [
                        "donor_policy",
                        "responsible_ai_guidance",
                        "country_context",
                    ],
                    "donor_id": "usaid",
                },
            },
            "state": {
                "donor_id": "usaid",
                "toc_draft": {"toc": {"brief": "Sample ToC"}},
                "logframe_draft": {
                    "indicators": [
                        {
                            "indicator_id": "IND_001",
                            "name": "Public servants trained on responsible AI",
                            "baseline": "0 people",
                            "target": "240 people",
                            "frequency": "quarterly",
                            "formula": "Count of trainees completing curriculum",
                            "definition": "Number of civil servants completing certified AI modules.",
                            "data_source": "Training attendance and certification registry",
                            "means_of_verification": "Certification registry and spot-check attendance files",
                            "owner": "MEL lead and implementing partner M&E team",
                            "disaggregation": ["sex", "age", "region"],
                            "result_level": "outcome",
                        },
                        {
                            "indicator_id": "IND_002",
                            "name": "Government agencies adopting AI governance SOPs",
                            "baseline": "TBD",
                            "target": "",
                            "frequency": "semiannual",
                            "data_source": "Ministerial SOP approval records",
                            "owner": "Program manager",
                            "result_level": "output",
                        },
                    ]
                },
                "quality_score": 9.1,
                "critic_score": 8.9,
                "needs_revision": False,
                "toc_validation": {"valid": True, "schema_name": "USAID_TOC"},
                "toc_generation_meta": {
                    "engine": "fallback:contract_synthesizer",
                    "llm_used": False,
                    "retrieval_used": True,
                    "citation_policy": {"threshold_mode": "donor_section"},
                },
                "architect_retrieval": {"enabled": True, "hits_count": 3, "namespace": "usaid_ads201"},
                "grounded_quality_gate": {
                    "mode": "strict",
                    "applicable": True,
                    "passed": True,
                    "blocking": False,
                    "summary": "runtime_grounded_signals_ok",
                    "reasons": [],
                },
                "critic_notes": {
                    "engine": "rules",
                    "rule_score": 8.9,
                    "llm_score": None,
                    "rule_checks": [
                        {"code": "TOC_SCHEMA_VALID", "status": "pass", "section": "toc"},
                        {"code": "TOC_CLAIM_CITATIONS", "status": "warn", "section": "toc"},
                        {"code": "LOGFRAME_CITATIONS_PRESENT", "status": "fail", "section": "logframe"},
                    ],
                    "fatal_flaws": [
                        {
                            "finding_id": "f1",
                            "status": "open",
                            "severity": "high",
                            "section": "toc",
                            "code": "X",
                            "message": "m",
                        },
                        {
                            "finding_id": "f2",
                            "status": "acknowledged",
                            "severity": "medium",
                            "section": "logframe",
                            "code": "Y",
                            "message": "m",
                            "source": "llm",
                            "label": "BASELINE_TARGET_MISSING",
                        },
                        {
                            "finding_id": "f3",
                            "status": "resolved",
                            "severity": "low",
                            "section": "toc",
                            "code": "Z",
                            "message": "m",
                        },
                    ],
                },
                "citations": [
                    {
                        "stage": "architect",
                        "citation_type": "rag_claim_support",
                        "citation_confidence": 0.81,
                        "confidence_threshold": 0.42,
                    },
                    {
                        "stage": "architect",
                        "citation_type": "rag_low_confidence",
                        "citation_confidence": 0.22,
                        "confidence_threshold": 0.42,
                    },
                    {
                        "stage": "architect",
                        "citation_type": "fallback_namespace",
                        "citation_confidence": 0.1,
                        "confidence_threshold": 0.42,
                    },
                    {"stage": "mel", "citation_type": "rag_result", "citation_confidence": 0.73},
                ],
            },
            "job_events": [
                {
                    "event_id": "q1",
                    "ts": "2026-02-24T10:00:00+00:00",
                    "type": "status_changed",
                    "to_status": "accepted",
                    "status": "accepted",
                },
                {
                    "event_id": "q2",
                    "ts": "2026-02-24T10:00:05+00:00",
                    "type": "status_changed",
                    "to_status": "running",
                    "status": "running",
                },
                {
                    "event_id": "q3",
                    "ts": "2026-02-24T10:01:00+00:00",
                    "type": "status_changed",
                    "to_status": "done",
                    "status": "done",
                },
            ],
        },
    )

    response = client.get(f"/status/{job_id}/quality")
    assert response.status_code == 200
    body = response.json()
    assert body["job_id"] == job_id
    assert body["status"] == "done"
    assert body["quality_score"] == 9.1
    assert body["critic_score"] == 8.9
    assert body["needs_revision"] is False
    assert body["strict_preflight"] is True
    assert body["terminal_status"] == "done"
    assert body["critic"]["fatal_flaw_count"] == 3
    assert body["critic"]["open_finding_count"] == 1
    assert body["critic"]["acknowledged_finding_count"] == 1
    assert body["critic"]["resolved_finding_count"] == 1
    assert body["critic"]["high_severity_fatal_flaw_count"] == 1
    assert body["critic"]["version_bindable_finding_count"] == 3
    assert body["critic"]["version_bound_finding_count"] == 0
    assert body["critic"]["version_binding_rate"] == 0.0
    assert body["critic"]["failed_rule_check_count"] == 1
    assert body["critic"]["warned_rule_check_count"] == 1
    assert body["critic"]["llm_finding_label_counts"]["BASELINE_TARGET_MISSING"] == 1
    assert body["citations"]["citation_count"] == 4
    assert body["citations"]["architect_citation_count"] == 3
    assert body["citations"]["mel_citation_count"] == 1
    assert body["citations"]["citation_type_counts"]["rag_claim_support"] == 1
    assert body["citations"]["citation_type_counts"]["rag_low_confidence"] == 1
    assert body["citations"]["citation_type_counts"]["fallback_namespace"] == 1
    assert body["citations"]["citation_type_counts"]["rag_result"] == 1
    assert body["citations"]["architect_citation_type_counts"]["rag_claim_support"] == 1
    assert body["citations"]["architect_citation_type_counts"]["rag_low_confidence"] == 1
    assert body["citations"]["architect_citation_type_counts"]["fallback_namespace"] == 1
    assert body["citations"]["mel_citation_type_counts"]["rag_result"] == 1
    assert body["citations"]["architect_claim_support_citation_count"] == 1
    assert body["citations"]["architect_claim_support_rate"] == 0.3333
    assert body["citations"]["architect_fallback_namespace_citation_count"] == 1
    assert body["citations"]["architect_fallback_namespace_citation_rate"] == 0.3333
    assert body["citations"]["mel_claim_support_citation_count"] == 1
    assert body["citations"]["mel_claim_support_rate"] == 1.0
    assert body["citations"]["mel_fallback_namespace_citation_count"] == 0
    assert body["citations"]["mel_fallback_namespace_citation_rate"] == 0.0
    assert body["citations"]["high_confidence_citation_count"] == 2
    assert body["citations"]["low_confidence_citation_count"] == 2
    assert body["citations"]["architect_rag_low_confidence_citation_count"] == 1
    assert body["citations"]["mel_rag_low_confidence_citation_count"] == 0
    assert body["citations"]["rag_low_confidence_citation_count"] == 1
    assert body["citations"]["fallback_namespace_citation_count"] == 1
    assert body["citations"]["fallback_namespace_citation_rate"] == 0.25
    assert body["citations"]["strategy_reference_citation_count"] == 0
    assert body["citations"]["strategy_reference_citation_rate"] == 0.0
    assert body["citations"]["retrieval_grounded_citation_count"] == 3
    assert body["citations"]["retrieval_grounded_citation_rate"] == 0.75
    assert body["citations"]["doc_id_present_citation_count"] == 0
    assert body["citations"]["doc_id_present_citation_rate"] == 0.0
    assert body["citations"]["retrieval_rank_present_citation_count"] == 0
    assert body["citations"]["retrieval_rank_present_citation_rate"] == 0.0
    assert body["citations"]["retrieval_confidence_present_citation_count"] == 4
    assert body["citations"]["retrieval_confidence_present_citation_rate"] == 1.0
    assert body["citations"]["retrieval_metadata_complete_citation_count"] == 0
    assert body["citations"]["retrieval_metadata_complete_citation_rate"] == 0.0
    assert body["citations"]["architect_doc_id_present_citation_count"] == 0
    assert body["citations"]["architect_doc_id_present_citation_rate"] == 0.0
    assert body["citations"]["architect_retrieval_rank_present_citation_count"] == 0
    assert body["citations"]["architect_retrieval_rank_present_citation_rate"] == 0.0
    assert body["citations"]["architect_retrieval_confidence_present_citation_count"] == 3
    assert body["citations"]["architect_retrieval_confidence_present_citation_rate"] == 1.0
    assert body["citations"]["architect_retrieval_metadata_complete_citation_count"] == 0
    assert body["citations"]["architect_retrieval_metadata_complete_citation_rate"] == 0.0
    assert body["citations"]["architect_signal_summary"]["architect_citation_count"] == 3
    assert body["citations"]["architect_signal_summary"]["evidence_signal_count"] == 3
    assert body["citations"]["architect_signal_summary"]["review_hint_count"] >= 1
    assert body["citations"]["mel_doc_id_present_citation_count"] == 0
    assert body["citations"]["mel_doc_id_present_citation_rate"] == 0.0
    assert body["citations"]["mel_retrieval_rank_present_citation_count"] == 0
    assert body["citations"]["mel_retrieval_rank_present_citation_rate"] == 0.0
    assert body["citations"]["mel_retrieval_confidence_present_citation_count"] == 1
    assert body["citations"]["mel_retrieval_confidence_present_citation_rate"] == 1.0
    assert body["citations"]["mel_retrieval_metadata_complete_citation_count"] == 0
    assert body["citations"]["mel_retrieval_metadata_complete_citation_rate"] == 0.0
    assert body["citations"]["non_retrieval_citation_count"] == 1
    assert body["citations"]["non_retrieval_citation_rate"] == 0.25
    assert body["citations"]["retrieval_expected"] is True
    assert body["citations"]["grounding_risk_level"] == "low"
    assert body["citations"]["traceability_complete_citation_count"] == 0
    assert body["citations"]["traceability_partial_citation_count"] == 0
    assert body["citations"]["traceability_missing_citation_count"] == 4
    assert body["citations"]["traceability_gap_citation_count"] == 4
    assert body["citations"]["traceability_gap_citation_rate"] == 1.0
    assert body["citations"]["architect_threshold_hit_rate"] == 0.3333
    assert body["architect_claims"]["claim_citation_count"] == 0
    assert body["architect_claims"]["claims_total"] == 0
    assert body["architect_claims"]["key_claims_total"] == 0
    assert body["architect_claims"]["fallback_claim_count"] == 0
    assert body["architect_claims"]["traceability_gap_citation_count"] == 0
    assert body["architect_claims"].get("threshold_hit_rate") is None
    assert body["architect_claims"].get("claim_coverage_ratio") is None
    assert body["architect_claims"].get("key_claim_coverage_ratio") is None
    assert body["architect_claims"].get("fallback_claim_ratio") is None
    assert body["architect"]["retrieval_enabled"] is True
    assert body["architect"]["retrieval_hits_count"] == 3
    assert body["architect"]["toc_schema_valid"] is True
    assert body["architect"]["citation_policy"]["threshold_mode"] == "donor_section"
    mel_summary = body.get("mel") or {}
    assert mel_summary.get("engine") in {"deterministic:retrieval_template", "llm:stub-mel-model", None}
    assert mel_summary.get("retrieval_namespace") in {None, "usaid_ads201"}
    assert mel_summary.get("retrieval_hits_count") in {None, 3}
    assert mel_summary.get("indicator_count") == 2
    assert mel_summary.get("baseline_coverage_rate") == 0.5
    assert mel_summary.get("target_coverage_rate") == 0.5
    assert mel_summary.get("frequency_coverage_rate") == 1.0
    assert mel_summary.get("formula_coverage_rate") == 0.5
    assert mel_summary.get("definition_coverage_rate") == 0.5
    assert mel_summary.get("data_source_coverage_rate") == 1.0
    assert mel_summary.get("disaggregation_coverage_rate") == 0.5
    assert mel_summary.get("means_of_verification_coverage_rate") == 0.5
    assert mel_summary.get("owner_coverage_rate") == 1.0
    assert mel_summary.get("result_level_coverage_rate") == 1.0
    assert mel_summary.get("smart_field_coverage_rate") == 0.7143
    assert mel_summary.get("risk_level") == "medium"
    assert mel_summary.get("baseline_placeholder_count") == 1
    assert mel_summary.get("target_placeholder_count") == 1
    assert mel_summary.get("missing_field_counts") == {
        "baseline": 1,
        "target": 1,
        "frequency": 0,
        "formula": 1,
        "definition": 1,
        "data_source": 0,
        "disaggregation": 1,
        "means_of_verification": 1,
        "owner": 0,
        "result_level": 0,
    }
    assert mel_summary.get("result_level_counts") == {"impact": 0, "outcome": 1, "output": 1, "unknown": 0}
    assert "mel_grounding_policy" in body
    export_contract = body.get("export_contract") or {}
    assert export_contract["template_key"] == "usaid"
    assert export_contract["status"] == "warning"
    assert "missing_required_toc_sections" in (export_contract.get("reasons") or [])
    grounded_gate = body.get("grounded_gate") or {}
    assert grounded_gate["mode"] == "strict"
    assert grounded_gate["blocking"] is False
    assert grounded_gate["passed"] is True
    assert body["preflight"]["risk_level"] == "medium"
    assert body["preflight"]["warning_count"] == 1
    assert body["preflight"]["warnings"][0]["code"] == "LOW_DOC_COVERAGE"
    assert body["readiness"]["preset_key"] == "usaid_gov_ai_kazakhstan"
    assert body["readiness"]["donor_id"] == "usaid"
    assert body["readiness"]["expected_doc_families"] == [
        "donor_policy",
        "responsible_ai_guidance",
        "country_context",
    ]
    assert body["readiness"]["present_doc_families"] == ["donor_policy", "country_context"]
    assert body["readiness"]["missing_doc_families"] == ["responsible_ai_guidance"]
    assert body["readiness"]["expected_count"] == 3
    assert body["readiness"]["loaded_count"] == 2
    assert body["readiness"]["coverage_rate"] == 0.6667
    assert body["readiness"]["inventory_total_uploads"] == 2
    assert body["readiness"]["inventory_family_count"] == 2
    assert body["review_readiness_summary"]["open_critic_findings"] >= 1
    assert body["review_readiness_summary"]["high_severity_open_findings"] >= 1
