"""
Wallet Clustering API Router - Cluster detection, funding paths, risk analysis.
Connects to /api/v1/wallet-clusters/*
"""

import logging
from datetime import UTC

from fastapi import APIRouter, HTTPException
from pydantic import BaseModel

router = APIRouter(prefix="/api/v1/wallet-clusters", tags=["wallet-clustering"])
logger = logging.getLogger(__name__)


class ClusterRequest(BaseModel):
    wallets: list[str]
    min_confidence: float = 0.3
    include_sleepers: bool = True


class FundingPathRequest(BaseModel):
    source: str
    target: str
    max_depth: int = 5


class BubblemapRequest(BaseModel):
    center_wallet: str
    depth: int = 2
    max_wallets: int = 250
    ai_deep_dive: bool = False


class ContractScanRequest(BaseModel):
    contract_address: str
    chain: str = "solana"
    detect_clusters: bool = True
    detect_bundles: bool = True


def _get_detector():
    try:
        from app.cluster_detection import ClusterDetectionPro

        return ClusterDetectionPro()
    except ImportError as e:
        raise HTTPException(status_code=503, detail=f"Module unavailable: {e}") from e


def _get_engine():
    try:
        from app.wallet_clustering import get_clustering_engine

        return get_clustering_engine()
    except ImportError as e:
        raise HTTPException(status_code=503, detail=f"Module unavailable: {e}") from e


@router.post("/detect")
async def detect_clusters(req: ClusterRequest):
    """Detect wallet clusters from a list of addresses (7 methods)."""
    if len(req.wallets) < 2:
        raise HTTPException(status_code=400, detail="Need at least 2 wallets")
    if len(req.wallets) > 100:
        raise HTTPException(status_code=400, detail="Max 100 wallets per request")

    detector = _get_detector()
    clusters = await detector.detect_clusters(
        wallets=req.wallets,
        min_confidence=req.min_confidence,
        include_sleepers=req.include_sleepers,
    )
    return {
        "total_wallets": len(req.wallets),
        "clusters_found": len(clusters),
        "clusters": [c.to_dict() for c in clusters],
    }


@router.post("/funding-path")
async def trace_funding_path(req: FundingPathRequest):
    """Trace the funding path between two wallets (BFS)."""
    detector = _get_detector()
    path = await detector.trace_funding_path(source=req.source, target=req.target, max_depth=req.max_depth)
    if path:
        return {"source": req.source, "target": req.target, "path": path.to_dict()}
    return {"source": req.source, "target": req.target, "path": None, "note": "No path found"}


@router.post("/scan")
async def scan_all():
    """Run all 4 clustering methods and return merged results."""
    engine = _get_engine()
    clusters = engine.find_all_clusters()
    return {"clusters": [c.to_dict() for c in clusters], "total_clusters": len(clusters)}


@router.get("/health")
async def clustering_health():
    cache_stats = {}
    gnn_status = {}
    spam_stats = {}
    try:
        from app.chain_cache import get_chain_cache

        cache_stats = await get_chain_cache().stats()
    except Exception:
        pass
    try:
        from app.fraud_gnn import get_fraud_gnn

        gnn_status = get_fraud_gnn().status()
    except Exception:
        gnn_status = {"error": "unavailable"}
    try:
        from app.spam_registry import get_spam_registry

        spam_stats = get_spam_registry().stats()
    except Exception:
        spam_stats = {"error": "unavailable"}
    return {
        "status": "ok",
        "service": "wallet-clustering-engine",
        "cache": cache_stats,
        "gnn": gnn_status,
        "spam_registry": spam_stats,
    }


@router.get("/report/{cluster_id}")
async def get_cluster_report(cluster_id: str):
    """Get detailed report for a specific cluster."""
    engine = _get_engine()
    report = engine.get_cluster_report(cluster_id)
    if not report:
        raise HTTPException(status_code=404, detail="Cluster not found")
    return report


@router.post("/bubble-map-data")
async def get_bubble_map_data(req: BubblemapRequest):
    """Generate bubble map data for a wallet.
    Supports up to 250 wallets deep. Optional AI deep dive for advanced forensics."""
    engine = _get_engine()
    depth = min(req.depth, 10)  # Cap depth at 10 to prevent infinite loops, but max_wallets handles the 250 limit
    data = engine.generate_bubble_map_data(
        center_wallet=req.center_wallet, depth=depth, max_wallets=min(req.max_wallets, 250)
    )

    # AI Deep Dive: If requested and cluster is large/suspicious, trigger AI analysis
    if req.ai_deep_dive and data.get("nodes") and len(data["nodes"]) >= 5:
        try:
            from app.ai_router import router as ai_router

            # Extract top suspicious wallets for AI context
            suspicious_wallets = [
                n for n in data["nodes"] if n.get("type") in ["scammer", "exchange"] or n.get("volume", 0) > 10000
            ][:10]

            prompt = f"""Analyze this wallet cluster for {req.center_wallet}.
            Center wallet: {req.center_wallet}
            Total connected wallets: {len(data["nodes"])}
            Suspicious nodes: {suspicious_wallets}

            Provide a 3-bullet point forensic breakdown:
            1. Primary risk vector (honeypot, bundle, wash trading, etc.)
            2. Key suspicious wallets and their roles
            3. Recommended action for the user

            Be concise, professional, and direct."""

            messages = [
                {
                    "role": "system",
                    "content": "You are an elite blockchain forensics analyst. Provide concise, actionable intelligence.",
                },
                {"role": "user", "content": prompt},
            ]

            result = await ai_router.chat_completion(  # type: ignore
                messages=messages, tier="T2", temperature=0.2, max_tokens=400, timeout=15.0
            )

            if "error" in result:
                data["ai_deep_dive_analysis"] = f"AI analysis failed: {result['error']}"
            else:
                data["ai_deep_dive_analysis"] = result.get("content", "AI analysis completed but returned empty.")
        except Exception as e:
            logger.warning(f"AI deep dive failed: {e}")
            data["ai_deep_dive_analysis"] = "AI analysis temporarily unavailable."

    return data


@router.post("/ai-forensic-breakdown")
async def get_ai_forensic_breakdown(req: BubblemapRequest):
    """
    Premium AI-driven forensic breakdown that dynamically pulls deeper if necessary.

    This is a unique feature that analyzes the initial cluster (up to 250 wallets) for
    risk vectors. If complex layering, high-risk patterns, or obfuscation tactics are
    detected, it automatically expands the search depth (up to 1000 wallets) to provide
    a comprehensive forensic breakdown that competitors lack.
    """
    engine = _get_engine()

    # Use the new AI forensic breakdown method
    breakdown = engine.generate_ai_forensic_breakdown(
        center_wallet=req.center_wallet,
        initial_depth=req.depth,
        initial_max_wallets=min(req.max_wallets, 250),
        max_expansion_depth=5,
        absolute_max_wallets=1000,
    )

    # If AI deep dive is explicitly requested, enrich with LLM analysis
    if req.ai_deep_dive and breakdown.get("total_wallets_analyzed", 0) > 0:
        try:
            from app.ai_router import router as ai_router

            prompt = f"""You are an elite blockchain forensics analyst. Analyze this wallet cluster.

CENTER WALLET: {req.center_wallet}
ANALYSIS MODE: {breakdown.get("analysis_mode")}
RISK SCORE: {breakdown.get("risk_score")}/1.0
RISK VECTORS: {", ".join(breakdown.get("risk_vectors", ["None"]))}
TOTAL WALLETS ANALYZED: {breakdown.get("total_wallets_analyzed")}
TOTAL CONNECTIONS: {breakdown.get("total_connections_analyzed")}

WALLET PROFILES (top 10 by volume):
{chr(10).join([f"- {w['address']}: {w['total_volume']} vol, {w['total_transactions']} txs" for w in breakdown.get("wallet_profiles", [])[:10]])}

Provide a concise, professional forensic breakdown:
1. Primary risk vector and obfuscation tactics used
2. Key suspicious wallets and their likely roles (funder, mule, cash-out)
3. Clear, actionable recommendation for the user

Be direct and avoid fluff."""

            messages = [
                {
                    "role": "system",
                    "content": "You are an elite blockchain forensics analyst. Provide concise, actionable intelligence.",
                },
                {"role": "user", "content": prompt},
            ]

            result = await ai_router.chat_completion(  # type: ignore
                messages=messages, tier="T2", temperature=0.2, max_tokens=600, timeout=20.0
            )

            if "error" in result:
                breakdown["ai_llm_analysis"] = f"AI analysis failed: {result['error']}"
            else:
                breakdown["ai_llm_analysis"] = result.get("content", "AI analysis completed but returned empty.")
        except Exception as e:
            logger.warning(f"AI LLM deep dive failed: {e}")
            breakdown["ai_llm_analysis"] = (
                "AI LLM analysis temporarily unavailable, but heuristic forensic data is provided."
            )

    return breakdown


@router.post("/contract-scan")
async def scan_contract_clusters(req: ContractScanRequest):
    """Scan a contract for wallet clusters among its holders.
    Finds real holder wallets via Helius, then detects clusters."""
    engine = _get_engine()
    detector = _get_detector()

    # Step 1: Get real holders from chain (multi-source fallback)
    holders: list[str] = []
    try:
        from app.unified_provider import get_unified_provider

        provider = get_unified_provider()
        holder_data = await provider.get_token_holders(req.contract_address, limit=30)
        holders = [h.get("address") for h in holder_data if h.get("address")]

        # Filter out exchange/DeFi infrastructure
        excluded_entities = []
        try:
            from app.entity_registry import get_entity_registry

            registry = get_entity_registry()
            holders, excluded_entities = registry.filter_infrastructure(holders)
        except Exception as e:
            logger.debug(f"Entity filter skipped: {e}")

    except Exception as e:
        logger.warning(f"Provider failed: {e}")

    # Step 2: Feed transactions for holders into engine
    if holders and req.detect_clusters:
        try:
            from app.unified_provider import get_unified_provider

            provider = get_unified_provider()
            for h in holders[:5]:  # Limit API calls - feed top 5 holders
                txs = await provider.get_wallet_transactions(h, limit=10)
                from datetime import datetime

                from app.wallet_clustering import Transaction

                for t in txs:
                    ts = t.get("timestamp")
                    dt = datetime.fromtimestamp(ts, tz=UTC) if ts else datetime.now(UTC)
                    engine.add_transaction(
                        Transaction(
                            signature=t.get("signature", ""),
                            timestamp=dt,
                            from_address=h,
                            to_address="unknown",
                            amount=0,
                            token="SOL",
                            program="system",
                        )
                    )
        except Exception as e:
            logger.warning(f"Feed failed: {e}")

    result = {
        "contract": req.contract_address,
        "chain": req.chain,
        "total_holders": len(holders) + len(excluded_entities),
        "holders": holders[:10],
        "excluded_infrastructure": [
            {"address": e.address, "entity": e.entity_name, "type": e.entity_type} for e in excluded_entities
        ],
        "holders_labeled": sum(1 for h in holders if engine._get_known_scammer_wallets()),
        "spam_check": {},
    }

    # Spam/sanctions check
    try:
        from app.spam_registry import get_spam_registry

        sr = get_spam_registry()
        result["spam_check"] = sr.check_token(req.contract_address, req.chain)
    except Exception as e:
        logger.debug(f"Spam check skipped: {e}")

    # Step 3: Detect clusters
    if req.detect_clusters and len(holders) >= 2:
        clusters = await detector.detect_clusters(wallets=holders[:50], min_confidence=0.3, include_sleepers=True)
        result["clusters_found"] = len(clusters)
        result["clusters"] = [c.to_dict() for c in clusters]

        # GNN fraud scoring on cluster wallets
        if clusters:
            try:
                from app.fraud_gnn import get_fraud_gnn

                gnn = get_fraud_gnn()
                all_wallets = [w for c in clusters for w in c.wallets]
                fps = [{"address": w, "tx_count": 1} for w in all_wallets[:20]]
                gnn_scores = gnn.score_wallets(fps)
                high_risk = [s for s in gnn_scores if s.get("fraud_probability", 0) >= 0.5]
                result["gnn_scoring"] = {
                    "wallets_scored": len(gnn_scores),
                    "high_risk_count": len(high_risk),
                    "avg_fraud_probability": round(
                        sum(s.get("fraud_probability", 0) for s in gnn_scores) / max(len(gnn_scores), 1),
                        4,
                    ),
                    "model": gnn.status().get("model_type", "unknown"),
                }
            except Exception as e:
                logger.debug(f"GNN scoring skipped: {e}")

    # Step 4: Bundle detection
    if req.detect_bundles and holders:
        try:
            from app.bundle_detector import get_bundle_detector

            bd = get_bundle_detector()
            bundle = await bd.detect(
                token_address=req.contract_address,
                chain=req.chain,
                holders=holder_data,
                transactions=None,  # TODO: feed real tx data
            )
            result["bundle_detection"] = {
                "is_bundled": bundle.is_bundled,
                "confidence": bundle.confidence,
                "risk_label": bundle.risk_label,
                "signals": {
                    "atomic_block": bundle.atomic_block_score,
                    "common_funder": bundle.common_funder_score,
                    "temporal": bundle.temporal_score,
                    "distribution_anomaly": bundle.distribution_anomaly_score,
                    "concentration": bundle.concentration_score,
                },
                "details": {
                    "top10_pct": bundle.top10_holder_pct,
                    "top3_pct": bundle.top3_holder_pct,
                    "identical_amounts": bundle.identical_amount_count,
                    "round_amounts": bundle.round_amount_count,
                    "holder_count": bundle.holder_count,
                    "common_funder": bundle.common_funder_address,
                },
            }
        except Exception as e:
            logger.warning(f"Bundle detection failed: {e}")

    return result
