# -*- coding: utf-8 -*-
"""STEP 7 — Citation verification (Gate 2 engine), FAIL-CLOSED + fallback source.

CourtListener is a single point of failure for the citation gate — the thing that
prevents AI-hallucinated-cite sanctions. So this verifier:

  1. FAILS CLOSED — if the source is down, rate-limited, or has no record, the
     citation NEVER becomes 'verified'. It stays blocked. We only set 'verified'
     when a source AFFIRMATIVELY reports the case exists and is good law.
  2. Has a FALLBACK source — if the primary (CourtListener) is unavailable, a
     secondary source is consulted before giving up.
  3. FL coverage caveat — confirm CourtListener (+fallback) actually cover Florida
     DCA + Supreme Court family-law authority before relying on this in production.

The two network methods (_verify_courtlistener / _verify_fallback) are the ONLY
places that touch the network and are the mock points in tests.
"""

import logging

from odoo import api, fields, models, _

_logger = logging.getLogger(__name__)

PARAM_CL_TOKEN = "familylaw.courtlistener_token"
PARAM_CL_ENDPOINT = "familylaw.courtlistener_endpoint"
DEFAULT_CL_ENDPOINT = "https://www.courtlistener.com/api/rest/v4/citation-lookup/"


class FamilyLawCitationVerifier(models.AbstractModel):
    _name = "familylaw.citation.verifier"
    _description = "Citation Verifier (fail-closed)"

    # --- network methods (MOCK THESE IN TESTS) ------------------------------
    def _verify_courtlistener(self, citation_text):
        """Primary source. Returns a dict {found, good_law, note} or raises on a
        transport/rate-limit error (which the caller treats as 'unavailable').
        Returning {'found': False} means the source AFFIRMATIVELY has no record."""
        import requests
        icp = self.env["ir.config_parameter"].sudo()
        endpoint = icp.get_param(PARAM_CL_ENDPOINT) or DEFAULT_CL_ENDPOINT
        token = icp.get_param(PARAM_CL_TOKEN)
        headers = {}
        if token:
            headers["Authorization"] = "Token %s" % token
        resp = requests.post(
            endpoint, headers=headers, data={"text": citation_text}, timeout=60,
        )
        resp.raise_for_status()
        data = resp.json()
        # CourtListener returns a list of matches; a non-empty match => found.
        matches = data if isinstance(data, list) else data.get("results", [])
        found = bool(matches)
        # good-law / treatment is not a simple flag in the public API; default to
        # found-implies-citable but flag for attorney. Conservative: good_law=found.
        return {
            "found": found,
            "good_law": found,
            "note": _("CourtListener returned %d match(es).") % len(matches or []),
        }

    def _verify_fallback(self, citation_text):
        """Secondary source (e.g. a secondary reporter API / Google Scholar gateway).
        Same contract as the primary. Not wired to a real provider by default —
        returns None ('no fallback configured') so the verifier fails closed."""
        return None

    # --- decision (fail-closed) ---------------------------------------------
    def verify_citation(self, citation):
        text = citation.citation_text or ""
        result = None
        used_source = False

        # primary
        try:
            result = self._verify_courtlistener(text)
            used_source = "courtlistener"
        except Exception as exc:  # noqa: BLE001
            _logger.warning("CourtListener unavailable for '%s': %s", text, exc)
            result = None

        # fallback only if primary unavailable or reported no record
        if result is None or not result.get("found"):
            try:
                fb = self._verify_fallback(text)
            except Exception as exc:  # noqa: BLE001
                _logger.warning("Fallback verifier unavailable: %s", exc)
                fb = None
            if fb is not None and (result is None or fb.get("found")):
                result = fb
                used_source = "fallback"

        # decide — only an affirmative found+good_law yields 'verified'
        if result is None:
            status = "unverified"
            note = _("Verification source(s) unavailable — FAILED CLOSED (stays blocked).")
            source = False
        elif result.get("found") and result.get("good_law"):
            status = "verified"
            note = result.get("note") or _("Verified.")
            source = used_source
        elif result.get("found") and not result.get("good_law"):
            status = "rejected"
            note = result.get("note") or _("Found but flagged as not good law.")
            source = used_source
        else:
            status = "not_found"
            note = result.get("note") or _("No record found — stays blocked.")
            source = used_source

        citation.write({
            "status": status,
            "verification_source": source or False,
            "verification_note": note,
            "verification_date": fields.Datetime.now(),
        })
        citation.message_post(
            body=_("Verification result: %(s)s (%(src)s). %(note)s",
                   s=status, src=source or _("none"), note=note)
        )
        return status
