"""bandito audit — data integrity checks."""

from __future__ import annotations

import json as json_mod
from typing import Any


def run_audit(
    project: str,
    *,
    json_output: bool = False,
) -> None:
    from ..storage import get_store
    from . import get_client

    checks: list[dict[str, Any]] = []

    # --- 1. Storage connectivity ---
    store = None
    try:
        store = get_store(project=project)
        err = store.validate()
        if err:
            checks.append(_check("Storage", "fail", err))
        else:
            backend = type(store).__name__
            checks.append(_check("Storage", "pass", f"{backend} connected"))
    except Exception as e:
        checks.append(_check("Storage", "fail", str(e)))

    # --- 2. Backend connectivity ---
    client = None
    try:
        from .api import BanditoClient
        client = BanditoClient()
        checks.append(_check("Backend", "pass", "connected"))
    except RuntimeError as e:
        checks.append(_check("Backend", "fail", str(e)))

    # --- 3. Project name consistency ---
    if store and client:
        try:
            check = _check_project_consistency(store, client, project)
            checks.append(check)
        except Exception as e:
            checks.append(_check("Project consistency", "fail", f"Error: {e}"))
    elif store and not client:
        checks.append(_check("Project consistency", "skip", "No backend connection"))

    # --- 4. Trace-evaluation linkage ---
    if store and client:
        try:
            check = _check_trace_linkage(store, client, project)
            checks.append(check)
        except Exception as e:
            checks.append(_check("Trace-evaluation linkage", "fail", f"Error: {e}"))
    elif store and not client:
        checks.append(_check("Trace-evaluation linkage", "skip", "No backend connection"))

    # --- Output ---
    if json_output:
        issues = sum(1 for c in checks if c["status"] in ("fail", "warn"))
        print(json_mod.dumps({
            "project": project,
            "checks": checks,
            "issues": issues,
        }, indent=2))
        return

    _print_audit(project, checks)


def _check(name: str, status: str, detail: str, evidence: list[str] | None = None) -> dict[str, Any]:
    return {
        "name": name,
        "status": status,  # "pass", "warn", "fail", "skip"
        "detail": detail,
        "evidence": evidence or [],
    }


def _check_project_consistency(store, client, project: str) -> dict[str, Any]:
    """Check that trace project names match evaluation project names."""
    from .retrieval import get_retrieval

    r = get_retrieval(project)
    traces = r.traces(tier="trace")
    storage_projects: set[str] = set()
    for t in traces:
        p = t.get("project")
        if p:
            storage_projects.add(p)

    all_projects = store.list_projects()
    similar = [p for p in all_projects if p.lower() == project.lower() and p != project]

    evals = r.evals()

    if similar:
        return _check(
            "Project consistency",
            "warn",
            f"Similar project names found that may cause evaluation mismatches",
            evidence=[f"Requested: \"{project}\""] + [f"Also exists: \"{p}\"" for p in similar],
        )

    if not storage_projects:
        return _check(
            "Project consistency",
            "warn",
            f"No traces found in storage for \"{project}\"",
        )

    if len(storage_projects) > 1:
        return _check(
            "Project consistency",
            "warn",
            f"Multiple project names in traces",
            evidence=[f"\"{p}\"" for p in sorted(storage_projects)],
        )

    return _check(
        "Project consistency",
        "pass",
        f"{len(traces)} traces, {len(evals)} evaluations for \"{project}\"",
    )


def _check_trace_linkage(store, client, project: str) -> dict[str, Any]:
    """Check that evaluations reference traces that exist in storage."""
    from .retrieval import get_retrieval

    r = get_retrieval(project)
    evals = r.evals()
    if not evals:
        return _check(
            "Trace-evaluation linkage",
            "pass",
            "No evaluations to check",
        )

    traces = r.traces(tier="trace")
    trace_ids = {t.get("trace_id") for t in traces}

    orphaned = [tid for tid in evals if tid not in trace_ids]

    if not orphaned:
        return _check(
            "Trace-evaluation linkage",
            "pass",
            f"All {len(evals)} evaluations link to existing traces",
        )

    return _check(
        "Trace-evaluation linkage",
        "warn",
        f"{len(orphaned)} evaluations reference traces not found in storage",
        evidence=orphaned[:10] + ([f"... and {len(orphaned) - 10} more"] if len(orphaned) > 10 else []),
    )


_STATUS_MARKS = {
    "pass": "\u2713",
    "warn": "~",
    "fail": "\u2717",
    "skip": "-",
}


def _print_audit(project: str, checks: list[dict[str, Any]]) -> None:
    print(f"\n  Bandito Audit: {project}\n")

    for c in checks:
        mark = _STATUS_MARKS.get(c["status"], "?")
        print(f"  {mark} {c['name']}: {c['detail']}")
        for e in c.get("evidence", []):
            print(f"    {e}")

    issues = sum(1 for c in checks if c["status"] in ("fail", "warn"))
    print()
    if issues == 0:
        print("  No issues found.")
    else:
        print(f"  {issues} issue{'s' if issues != 1 else ''} found.")
    print()
