# -*- coding: utf-8 -*-
"""STEP 7 tests — citation gate (Gate 2): fail-closed verification + filing block.

    odoo -d <db> -u activeblue_familylaw --test-enable \
         --test-tags familylaw_step7 --stop-after-init

External calls ALWAYS mocked: the verifier's _verify_courtlistener / _verify_fallback
and the AI client's _call_provider. Tests assert on Odoo state:
  * verified only on an affirmative found+good_law;
  * primary down -> fallback used;
  * both down -> stays unverified (FAIL CLOSED);
  * not found -> not_found; bad law -> rejected;
  * filing BLOCKED on any unverified citation, allowed when all verified;
  * research loop produces an ai_draft memo and verifies proposed citations.
"""

from unittest.mock import patch

from odoo.tests.common import TransactionCase, new_test_user, tagged
from odoo.exceptions import UserError

VERIFIER = ("odoo.addons.activeblue_familylaw_v2.models.familylaw_verifier."
            "FamilyLawCitationVerifier")
CLIENT = "odoo.addons.activeblue_familylaw_v2.models.familylaw_ai.FamilyLawAIClient"


def _ai_response(text="MEMO", citations=None, pt=10, ct=20):
    return {"text": text, "usage": {"input_tokens": pt, "output_tokens": ct},
            "citations": citations or []}


@tagged("post_install", "-at_install", "familylaw", "familylaw_step7")
class TestStep7CitationGate(TransactionCase):

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.attorney = new_test_user(
            cls.env, login="fl_atty7", name="Attorney 7",
            email="atty7@example.com",
            groups="base.group_user,activeblue_familylaw_v2.group_familylaw_attorney",
        )
        cls.partner = cls.env["res.partner"].create({"name": "Cite Client"})
        cls.case = cls.env["familylaw.case"].create({
            "name": "Cite Matter", "client_id": cls.partner.id,
            "case_type": "support_modification",
        })
        cls.proc = cls.case.proceeding_ids[0]
        cls.Doc = cls.env["familylaw.document"]
        cls.Cit = cls.env["familylaw.citation"]
        icp = cls.env["ir.config_parameter"].sudo()
        icp.set_param("familylaw.anthropic_api_key", "k")
        icp.set_param("familylaw.model", "claude-sonnet-4-6")

    def _doc(self):
        return self.Doc.create({
            "name": "Brief", "proceeding_id": self.proc.id,
            "document_type": "pleading", "source": "manual",
        })

    def _cite(self, doc, text="Smith v. Jones, 1 So.3d 2 (Fla. 3d DCA 2010)"):
        return self.Cit.create({"document_id": doc.id, "citation_text": text})

    # --- verification decisions ---------------------------------------------
    def test_01_primary_found_good_law_verified(self):
        doc = self._doc(); cite = self._cite(doc)
        with patch(VERIFIER + "._verify_courtlistener",
                   return_value={"found": True, "good_law": True, "note": "ok"}):
            cite.action_verify()
        self.assertEqual(cite.status, "verified")
        self.assertEqual(cite.verification_source, "courtlistener")

    def test_02_not_found_stays_blocked(self):
        doc = self._doc(); cite = self._cite(doc)
        with patch(VERIFIER + "._verify_courtlistener",
                   return_value={"found": False, "good_law": False}), \
             patch(VERIFIER + "._verify_fallback", return_value=None):
            cite.action_verify()
        self.assertEqual(cite.status, "not_found")
        self.assertFalse(cite.is_verified)

    def test_03_bad_law_rejected(self):
        doc = self._doc(); cite = self._cite(doc)
        with patch(VERIFIER + "._verify_courtlistener",
                   return_value={"found": True, "good_law": False}):
            cite.action_verify()
        self.assertEqual(cite.status, "rejected")

    # --- fail closed --------------------------------------------------------
    def test_04_primary_down_no_fallback_stays_unverified(self):
        doc = self._doc(); cite = self._cite(doc)

        def _down(*a, **k):
            raise ConnectionError("CourtListener 503")

        with patch(VERIFIER + "._verify_courtlistener", _down), \
             patch(VERIFIER + "._verify_fallback", return_value=None):
            cite.action_verify()
        self.assertEqual(cite.status, "unverified")  # FAIL CLOSED
        self.assertFalse(cite.is_verified)

    def test_05_primary_down_fallback_verifies(self):
        doc = self._doc(); cite = self._cite(doc)

        def _down(*a, **k):
            raise ConnectionError("down")

        with patch(VERIFIER + "._verify_courtlistener", _down), \
             patch(VERIFIER + "._verify_fallback",
                   return_value={"found": True, "good_law": True, "note": "fb"}):
            cite.action_verify()
        self.assertEqual(cite.status, "verified")
        self.assertEqual(cite.verification_source, "fallback")

    def test_06_both_down_fail_closed(self):
        doc = self._doc(); cite = self._cite(doc)

        def _down(*a, **k):
            raise ConnectionError("down")

        with patch(VERIFIER + "._verify_courtlistener", _down), \
             patch(VERIFIER + "._verify_fallback", side_effect=_down):
            cite.action_verify()
        self.assertEqual(cite.status, "unverified")

    # --- attorney attestation -----------------------------------------------
    def test_07_attorney_attest(self):
        doc = self._doc(); cite = self._cite(doc)
        cite.with_user(self.attorney).action_attorney_verify()
        self.assertEqual(cite.status, "verified")
        self.assertEqual(cite.verification_source, "attorney")

    def test_08_attest_requires_attorney(self):
        doc = self._doc(); cite = self._cite(doc)
        para = new_test_user(
            self.env, login="fl_para7", name="Para 7", email="p7@example.com",
            groups="base.group_user,activeblue_familylaw_v2.group_familylaw_user",
        )
        with self.assertRaises(UserError):
            cite.with_user(para).action_attorney_verify()

    # --- THE FILING GATE (Gate 2) -------------------------------------------
    def _approve(self, doc):
        doc.action_submit_for_review()
        doc.with_user(self.attorney).action_approve()

    def test_09_filing_blocked_with_unverified_cite(self):
        doc = self._doc()
        self._cite(doc)  # unverified
        self._approve(doc)
        with self.assertRaises(UserError):
            doc.with_user(self.attorney).action_mark_filed()
        self.assertNotEqual(doc.state, "filed")

    def test_10_filing_allowed_when_all_verified(self):
        doc = self._doc()
        cite = self._cite(doc)
        with patch(VERIFIER + "._verify_courtlistener",
                   return_value={"found": True, "good_law": True}):
            cite.action_verify()
        self._approve(doc)
        doc.with_user(self.attorney).action_mark_filed()
        self.assertEqual(doc.state, "filed")

    def test_11_request_filing_blocked_unverified(self):
        doc = self._doc()
        self._cite(doc)
        self._approve(doc)
        with self.assertRaises(UserError):
            doc.action_request_filing()

    def test_12_doc_with_no_citations_can_file(self):
        doc = self._doc()
        self._approve(doc)
        doc.with_user(self.attorney).action_mark_filed()
        self.assertEqual(doc.state, "filed")

    def test_13_send_blocked_with_unverified_cite(self):
        doc = self._doc()
        self._cite(doc)
        self._approve(doc)
        with self.assertRaises(UserError):
            doc.with_user(self.attorney).action_mark_sent()

    def test_14_unverified_count(self):
        doc = self._doc()
        c1 = self._cite(doc, "A v. B")
        self._cite(doc, "C v. D")
        self.assertEqual(doc.unverified_citation_count, 2)
        with patch(VERIFIER + "._verify_courtlistener",
                   return_value={"found": True, "good_law": True}):
            c1.action_verify()
        self.assertEqual(doc.unverified_citation_count, 1)

    # --- research loop ------------------------------------------------------
    def test_15_research_loop_produces_ai_draft(self):
        cites = [{"citation_text": "Doe v. Roe, 9 So.3d 9 (Fla. 2012)",
                  "proposition": "p"}]
        with patch(CLIENT + "._call_provider",
                   return_value=_ai_response(citations=cites)), \
             patch(VERIFIER + "._verify_courtlistener",
                   return_value={"found": True, "good_law": True}):
            doc = self.env["familylaw.research.agent"].run(self.proc, "test question")
        self.assertEqual(doc.state, "ai_draft")
        self.assertEqual(len(doc.citation_ids), 1)
        self.assertEqual(doc.citation_ids.status, "verified")

    def test_16_research_loop_unverified_blocks_filing(self):
        cites = [{"citation_text": "Fake v. Hallucination, 0 So.0d 0", "proposition": "p"}]
        with patch(CLIENT + "._call_provider",
                   return_value=_ai_response(citations=cites)), \
             patch(VERIFIER + "._verify_courtlistener",
                   return_value={"found": False, "good_law": False}), \
             patch(VERIFIER + "._verify_fallback", return_value=None):
            doc = self.env["familylaw.research.agent"].run(self.proc, "q")
        self.assertEqual(doc.citation_ids.status, "not_found")
        self._approve(doc)
        with self.assertRaises(UserError):
            doc.with_user(self.attorney).action_mark_filed()
