"""Phase B — Business-profile detection + per-document checks.

Covers (in the original `_run_compliance_check`):
  - Step 2  Detect business profile (with optional homepage merge for
            P16 keywords)
  - Step 3  Run regex + MC + LLM checks on each submitted document
            (`_check_single`), applying skip rules + profile filter
            + placement findings
"""

from __future__ import annotations

import logging
import os
import re as _re
from dataclasses import asdict

import httpx

from ._constants import _CAPTURE_ONLY
from ._helpers import (
    _apply_profile_filter,
    _doc_type_label,
    _get_skip_types,
    _update,
)
from ._single_check import _check_single

logger = logging.getLogger(__name__)


async def run_phase_b(state: dict) -> None:
    """Detect business profile + check each document. Mutates state in place."""
    check_id = state["check_id"]
    req = state["req"]
    doc_texts = state["doc_texts"]
    doc_entries = state["doc_entries"]
    placement_findings = state["placement_findings"]

    # Step 2: Detect business profile (35-40%)
    from compliance.services.business_profiler import detect_business_profile
    _update(check_id, "Geschaeftsmodell wird erkannt...", 37)
    # P16: Homepage-Text mit fuer Profile-Detection (no_direct_sales
    # B2B-Indikatoren wie "CE-Zertifizierung" / "Schulungen" stehen oft
    # nur im Homepage-Menue, nicht im Pflichttext).
    profile_input = dict(doc_texts)
    try:
        base_url = ""
        for e in doc_entries:
            if e.get("url"):
                from urllib.parse import urlparse
                p = urlparse(e["url"])
                if p.scheme and p.netloc:
                    base_url = f"{p.scheme}://{p.netloc}/"
                    break
        if base_url:
            async with httpx.AsyncClient(
                timeout=8.0, follow_redirects=True,
                headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) "
                         "AppleWebKit/537.36 HeadlessChrome/120.0.0.0"},
            ) as _hc:
                _hr = await _hc.get(base_url)
                if _hr.status_code == 200 and "text/html" in _hr.headers.get(
                        "content-type", ""):
                    _html = _hr.text[:60000]
                    _html = _re.sub(r"<script[^>]*>.*?</script>", " ",
                                    _html, flags=_re.DOTALL | _re.IGNORECASE)
                    _html = _re.sub(r"<style[^>]*>.*?</style>", " ",
                                    _html, flags=_re.DOTALL | _re.IGNORECASE)
                    _html = _re.sub(r"<[^>]+>", " ", _html)
                    _html = _re.sub(r"\s+", " ", _html).strip()
                    if len(_html.split()) > 30:
                        profile_input["__homepage"] = _html[:20000]
                        logger.info("P16 homepage merged for profile: %d words",
                                    len(_html.split()))
    except Exception as e:
        logger.debug("homepage fetch for profile failed: %s", e)
    profile = await detect_business_profile(profile_input)
    profile_dict = asdict(profile)

    # Step 3: Check each document
    from ..agent_doc_check_routes import CheckItem, DocCheckResult
    results: list[DocCheckResult] = []
    total_findings = 0
    use_agent_flag = req.use_agent or os.getenv(
        "COMPLIANCE_USE_AGENT", "false",
    ).lower() == "true"

    # Filter out doc_types that don't apply to this business profile
    skip_types = _get_skip_types(profile)

    # Derive business_scope hints for the MC filter (O1 — Doc-type Scope-Flag).
    # MCs that explicitly require a feature (e.g. 'biometric_processing',
    # 'ai_decision_making', 'child_targeting') get dropped when the
    # detected profile doesn't declare it.
    business_scope: set[str] = set()
    for svc in (getattr(profile, "detected_services", []) or []):
        business_scope.add(str(svc).lower())
    if (getattr(profile, "business_type", "") or "").lower() == "b2c":
        business_scope.add("b2c")
    if getattr(profile, "has_online_shop", False):
        business_scope.add("ecommerce")
    if getattr(profile, "is_regulated_profession", False):
        business_scope.add("regulated_profession")

    # Document checks: 40-80%
    n_entries = max(1, len(doc_entries))
    for i, entry in enumerate(doc_entries):
        text = entry["text"]
        doc_type = entry["doc_type"]
        label = _doc_type_label(doc_type)
        url = entry["url"]

        if doc_type in skip_types:
            results.append(DocCheckResult(
                label=label, url=url, doc_type=doc_type,
                error=skip_types[doc_type],
            ))
            continue

        # A: Capture-only — Text ist via doc_entries schon im Snapshot (für die
        # Cross-Doc-Reconciliation B); hier NICHT scoren (keine eigene
        # Checkliste → sonst irreführende 0%-Zeile).
        if doc_type in _CAPTURE_ONLY:
            results.append(DocCheckResult(
                label=label, url=url, doc_type=doc_type,
                error="Erfasst für Cross-Dokument-Abgleich (nicht einzeln bewertet).",
            ))
            continue

        pct = int(40 + (i / n_entries) * 40)
        _update(check_id, f"Pruefen {i+1}/{n_entries}: {label}...", pct)

        if not text or len(text) < 50:
            # P15: duplicate doc that was deduped against a primary doc
            if entry.get("dup_of"):
                results.append(DocCheckResult(
                    label=label, url="", doc_type=doc_type,
                    error=f"Nicht separat vorhanden — wird im Dokument "
                          f"'{_doc_type_label(entry['dup_of'])}' "
                          f"mit-geprueft.",
                ))
                continue
            # P24: DSB-Kontakt ist Pflichtangabe in der DSE (Art. 13(1)(b)
            # DSGVO) — wenn kein separates DSB-Dokument vorliegt, ist das
            # KEIN Fehler. DSB-Pruefung passiert ohnehin in der DSE.
            if doc_type == "dsb" and not (entry.get("url") or "").strip():
                results.append(DocCheckResult(
                    label=label, url="", doc_type=doc_type,
                    error="Nicht separat vorhanden — DSB-Kontaktdaten "
                          "werden in der Datenschutzerklaerung als "
                          "Pflichtangabe nach Art. 13(1)(b) DSGVO geprueft.",
                ))
                continue
            # Empty entry — either from auto-discovery padding (no URL
            # to fetch) or from a fetch that returned nothing. If there
            # was a URL we keep the error so the user knows the fetch
            # failed; otherwise let the padding step label it
            # 'Nicht eingereicht' / 'Auf der Website nicht gefunden'.
            if (entry.get("url") or "").strip():
                results.append(DocCheckResult(
                    label=label, url=url, doc_type=doc_type,
                    error="Kein Text vorhanden oder zu kurz",
                ))
            continue

        result = await _check_single(
            text, doc_type, label, url,
            entry["word_count"], use_agent_flag,
            business_scope=business_scope,
            business_profile={"no_direct_sales": getattr(profile, "no_direct_sales", False)},
        )

        # Apply profile context filter
        result = _apply_profile_filter(result, profile, doc_type)

        # Add placement findings — but only if the regex checks confirm
        # the text doesn't match. If completeness >= 50%, the text IS the
        # right doc_type despite missing cross-search keywords.
        if result.completeness_pct < 50:
            for pf in placement_findings:
                if pf.get("doc_type") == doc_type:
                    result.checks.insert(0, CheckItem(**{
                        k: v for k, v in pf.items() if k != "doc_type"
                    }))

        results.append(result)
        total_findings += result.findings_count

    state["profile"] = profile
    state["profile_dict"] = profile_dict
    state["business_scope"] = business_scope
    state["results"] = results
    state["total_findings"] = total_findings
