"""Trust API — Bayesian trust data, domain management, and explanations."""

from __future__ import annotations

from fastapi import APIRouter, HTTPException, Request
from pydantic import BaseModel

from agntspace.trust.service import (
    _level_from_score,
    get_bayesian_config,
)

router = APIRouter(prefix="/trust", tags=["trust"])


class TrustLevelUpdate(BaseModel):
    domain: str
    level: str


class DomainAdd(BaseModel):
    domain: str
    level: str = "Advisor"


@router.get("")
async def get_trust_data(request: Request) -> dict:
    """Get structured trust data with Bayesian scores and change log."""
    trust = request.app.state.trust_service
    rows = trust.get_trust_data()
    changes = trust.get_recent_changes()
    cfg = get_bayesian_config()

    # Enrich each domain with effective score and path-to-next
    enriched = []
    for row in rows:
        score = trust._make_beta_score(row)  # noqa: SLF001
        hl = trust._half_life_for_domain(row["domain"])  # noqa: SLF001
        eff = score.effective_score(hl, trust._prior_mean())  # noqa: SLF001
        level = _level_from_score(eff, cfg.level_thresholds)
        enriched.append({
            **row,
            "score": round(score.mean, 4),
            "effective_score": round(eff, 4),
            "level": level,
        })

    return {
        "domains": enriched,
        "changes": changes[-20:],
        "levels": ["Observer", "Advisor", "Assistant", "Partner"],
        "level_thresholds": list(cfg.level_thresholds),
    }


@router.get("/explain/{domain}")
async def explain_trust(request: Request, domain: str) -> dict:
    """Detailed trust explanation for a domain — score, decay, path to next level."""
    trust = request.app.state.trust_service
    return trust.explain_domain(domain)


@router.put("/level")
async def update_trust_level(request: Request, body: TrustLevelUpdate) -> dict:
    """Manually set trust level for a domain (sets score to midpoint of target band)."""
    trust = request.app.state.trust_service
    if body.level not in ("Observer", "Advisor", "Assistant", "Partner"):
        raise HTTPException(status_code=400, detail=f"Invalid level: {body.level}")

    cfg = get_bayesian_config()
    rows = trust.get_trust_data()
    found = False
    for row in rows:
        if row["domain"].lower() == body.domain.lower():
            old = row.get("level", "Advisor")
            # Set alpha/beta to the migration values for the target level
            migration = cfg.migration.get(body.level, {"alpha": 3.7, "beta": 8.7})
            row["alpha"] = migration["alpha"]
            row["beta_param"] = migration["beta"]
            row["level"] = body.level
            from datetime import UTC, datetime
            row["last_updated"] = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
            found = True
            break

    if not found:
        raise HTTPException(status_code=404, detail=f"Domain not found: {body.domain}")

    trust._write_trust_table(rows)  # noqa: SLF001
    trust.invalidate_cache()
    return {"domain": body.domain, "old_level": old, "new_level": body.level}


@router.post("/domain")
async def add_domain(request: Request, body: DomainAdd) -> dict:
    """Add a new trust domain with the global prior."""
    trust = request.app.state.trust_service
    rows = trust.get_trust_data()

    for row in rows:
        if row["domain"].lower() == body.domain.lower():
            raise HTTPException(status_code=409, detail=f"Domain already exists: {body.domain}")

    cfg = get_bayesian_config()
    migration = cfg.migration.get(body.level, {"alpha": 3.7, "beta": 8.7})
    rows.append({
        "domain": body.domain,
        "agent": "",
        "level": body.level,
        "alpha": migration["alpha"],
        "beta_param": migration["beta"],
        "completed": 0,
        "good": 0,
        "needs_improvement": 0,
        "redo": 0,
        "last_updated": "",
    })
    trust._write_trust_table(rows)  # noqa: SLF001
    trust.invalidate_cache()
    return {"domain": body.domain, "level": body.level}


@router.delete("/domain/{domain}")
async def remove_domain(request: Request, domain: str) -> dict:
    """Remove a trust domain."""
    trust = request.app.state.trust_service
    rows = trust.get_trust_data()
    new_rows = [r for r in rows if r["domain"].lower() != domain.lower()]
    if len(new_rows) == len(rows):
        raise HTTPException(status_code=404, detail=f"Domain not found: {domain}")
    trust._write_trust_table(new_rows)  # noqa: SLF001
    trust.invalidate_cache()
    return {"domain": domain, "status": "removed"}


# ── Domain discovery (architectural gap #3, 2026-04-18) ─────────────
#
# The discovery service walks the decision log, finds contract actions
# mapped to "general", clusters them, and proposes new domains. The
# engine is read-only by default — endpoints below surface proposals
# and accept user decisions. Approving a proposal routes through the
# skill-school proposal flow so domains.yaml is written with a review
# audit trail, never silently patched.


class DomainProposalDismiss(BaseModel):
    signature: str  # cluster fingerprint — "a|b|c" sorted action names


@router.get("/domain-proposals")
async def list_domain_proposals(request: Request) -> dict:
    """Return auto-discovered domain proposals for user review.

    The engine re-runs on each call — cheap for small decision logs,
    acceptable for large ones (queries are indexed on ts + correlation_id).
    Proposals are computed fresh so new chains immediately influence the
    cluster shape; no staleness, no caching to reason about.
    """
    engine = getattr(request.app.state, "trust_domain_discovery", None)
    if engine is None:
        return {"proposals": [], "available": False}
    try:
        proposals = await engine.discover()
    except Exception:
        raise HTTPException(
            status_code=503,
            detail="discovery engine unavailable",
        ) from None
    return {
        "available": True,
        "proposals": [
            {
                "suggested_name": p.suggested_name,
                "contract_actions": p.contract_actions,
                "total_occurrences": p.total_occurrences,
                "distinct_correlations": p.distinct_correlations,
                "shared_affix": p.shared_affix,
                "justification": p.justification,
                "co_occurrence_pairs": p.co_occurrence_pairs,
                "signature": "|".join(sorted(p.contract_actions)),
            }
            for p in proposals
        ],
    }


@router.post("/domain-proposals/dismiss")
async def dismiss_domain_proposal(request: Request, body: DomainProposalDismiss) -> dict:
    """Mark a proposal signature as dismissed.

    The engine doesn't persist dismissed signatures itself — this endpoint
    records to the trust_service's in-process dismissal set so subsequent
    `/domain-proposals` calls suppress the cluster for the cooldown window.
    Persistence across restarts is a follow-up (likely a SQLite table
    keyed by signature + timestamp).
    """
    engine = getattr(request.app.state, "trust_domain_discovery", None)
    if engine is None:
        raise HTTPException(status_code=503, detail="discovery engine unavailable")
    # Ephemeral dismissal — tracked on the engine instance. Process restart
    # resets. See the follow-up note in domain_discovery.py for persistence.
    engine._dismissed.add(body.signature)  # noqa: SLF001
    return {"signature": body.signature, "status": "dismissed"}
