"""Cookie-Policy v3-Pipeline — analog zu impressum/v3_engine.py.

MC-Laden DELEGIERT an die Main-Tool-Engine (rag_document_checker._load_controls,
doc_type='cookie'): eine Quelle der Wahrheit inkl. P72-Scope, check_type='text'
und fits_doc_type/scope_requires. KEINE parallele Roh-Query mehr. Danach
deterministischer Keyword-Check + Embedding-Match + Boost-Override.
Zusaetzlich ein subtraktives Sektor-Gate (Branchen-Prefix) am Agent-Rand.
"""

from __future__ import annotations

import logging
from typing import Any

from .regex_boost import boost_matches_db_mc, compute_regex_boosts

logger = logging.getLogger(__name__)

# Branchen-Prefix -> erwarteter Scope-Token (reuse aus dem Mail-V2-Filter).
try:
    from compliance.services.mail_render_v2._scope_filter import (
        SECTOR_PREFIXES,
    )
except Exception:  # pragma: no cover
    SECTOR_PREFIXES = {}


async def run_v3_pipeline(
    text: str, business_scope: set[str], db_url: str = "",
) -> tuple[list[dict[str, Any]], dict[str, Any]]:
    if not text or len(text) < 100:
        return [], {"reason": "text too short"}

    # Layer 0: meine Pattern-Boosts
    boosts = compute_regex_boosts(text)
    boost_field_ids = sorted(boosts)

    # Layer 1: MC-Laden DELEGIERT an die Main-Tool-Engine (Scope-Schutz
    # inklusive). Danach subtraktives Sektor-Gate am Agent-Rand.
    try:
        from compliance.services.rag_document_checker import _load_controls
        controls = await _load_controls("cookie", db_url, 0, business_scope)
    except Exception as e:
        logger.warning("cookie v3 load via main-tool engine failed: %s", e)
        controls = []
    _normalize_criteria(controls)
    controls, sector_dropped = _filter_sector(controls, business_scope)
    # Artefakt-Gate: Controls ohne COOKIE_POLICY-Artefakt (Security/TOM/Audit,
    # Banner) raus — sie gehoeren zu anderem Pruefer/Artefakt und erzeugen sonst
    # Unsinn-Findings. Siehe _classification_gate.
    routed_out: list[dict[str, Any]] = []
    try:
        from ._classification_gate import apply_gate, load_cookie_gate
        controls, routed_out = apply_gate(controls, await load_cookie_gate(db_url))
    except Exception as e:
        logger.warning("cookie classification gate skipped: %s", e)
    results: list[dict[str, Any]] = []
    if controls:
        try:
            from compliance.services.rag_document_checker import (
                _check_mc_deterministic,
            )
            text_lower = text.lower().replace("\xad", "")
            for mc in controls:
                r = _check_mc_deterministic(text_lower, mc)
                if r:
                    r["_pass_criteria"] = mc.get("pass_criteria")
                    r["_fail_criteria"] = mc.get("fail_criteria")
                    results.append(r)
        except Exception as e:
            logger.warning("layer-1 keyword check failed: %s", e)

    # Layer 2: Embedding-Match für failed MCs
    failed_for_embed = [
        c for c, r in zip(controls, results)
        if r and not r.get("passed")
    ]
    if failed_for_embed:
        try:
            from compliance.services.mc_embedding_matcher import (
                ensure_mc_embeddings, embedding_match,
            )
            await ensure_mc_embeddings()
            semantic_passes = await embedding_match(
                text, failed_for_embed, doc_type="cookie",
            )
            if semantic_passes:
                for r in results:
                    cid = r.get("control_id")
                    if cid in semantic_passes and not r.get("passed"):
                        r["passed"] = True
                        r["matched_text"] = "[layer-2 embedding match]"
                        r["source"] = (r.get("source") or "") + "+embedding"
        except Exception as e:
            logger.warning("layer-2 embedding skipped: %s", e)

    # Layer 0 Boost-Override
    boost_overrides = 0
    for r in results:
        if r.get("passed"):
            continue
        pass_crit = r.get("_pass_criteria") or []
        fail_crit = r.get("_fail_criteria") or []
        if not pass_crit and not fail_crit:
            pass_crit = [r.get("hint") or r.get("label") or ""]
        matched_field = boost_matches_db_mc(boosts, pass_crit, fail_crit)
        if matched_field:
            r["passed"] = True
            r["matched_text"] = f"[regex-boost layer 0 — {matched_field}]"
            r["source"] = (r.get("source") or "") + "+regex_boost"
            boost_overrides += 1

    layer_1_pass = sum(1 for r in results if r.get("passed")
                       and "+regex_boost" not in (r.get("source") or "")
                       and "+embedding" not in (r.get("source") or ""))
    telemetry = {
        "layer_0_field_hits": len(boost_field_ids),
        "layer_0_field_ids": boost_field_ids,
        "layer_1_pass": layer_1_pass,
        "layer_0_boost_overrides": boost_overrides,
        "total_mcs": len(results),
        "sector_dropped": sector_dropped,
        "artifact_gated": len(routed_out),
    }
    return results, telemetry


def _normalize_criteria(controls: list[dict[str, Any]]) -> None:
    """asyncpg liefert JSONB-Spalten als Roh-String → zu Listen parsen."""
    import json
    for c in controls:
        for key in ("pass_criteria", "fail_criteria"):
            v = c.get(key)
            if isinstance(v, list):
                continue
            if isinstance(v, str):
                try:
                    parsed = json.loads(v)
                    c[key] = parsed if isinstance(parsed, list) else [v]
                except Exception:
                    c[key] = [v] if v else []
            else:
                c[key] = []


def _filter_sector(
    controls: list[dict[str, Any]],
    business_scope: set[str],
) -> tuple[list[dict[str, Any]], int]:
    """Subtraktives Sektor-Gate: MCs deren control_id-Prefix eine Branche
    bezeichnet (FIN/GOV/MED/INS/EDU/LEG/REL/POL), die NICHT im business_scope
    liegt, werden verworfen — sonst tauchen z.B. GOV-MCs bei einem
    OEM/Zulieferer als Finding auf. Reuse der SECTOR_PREFIXES."""
    scope_lc = {s.lower() for s in (business_scope or set())}
    kept: list[dict[str, Any]] = []
    dropped = 0
    for c in controls:
        cid = c.get("control_id") or ""
        prefix = cid.split("-")[0].upper() if "-" in cid else ""
        required = SECTOR_PREFIXES.get(prefix)
        if required and not (scope_lc & required):
            dropped += 1
            continue
        kept.append(c)
    if dropped:
        logger.info("cookie v3 sector-filter: -%d Branchen-MCs", dropped)
    return kept, dropped


