"""
Security Intelligence Router v2 — Complete Crypto Security Stack
===================================================================

All crypto security modules exposed via REST:
  • Contract: bytecode scan + deep scan (Slither + Mythril)
  • Wallet: reputation + threat intel + anomaly detection
  • Graph: clustering + fund flow + cross-chain correlation
  • Mempool: real-time attack detection + sentinel control
  • Threat: OFAC + local blocklist + batch lookup
  • Dashboard: alert aggregation + monitoring + scoring
  • ML: anomaly detection on wallets + token metrics
  • CrossChain: wallet linking across chains

Updated 2026-05-08 for world-class crypto intelligence.
"""

from __future__ import annotations

# ═══ LAZY IMPORTS (deferred to first use — saves ~2.2s cold start) ═══
import importlib as _il
import os
from datetime import UTC, datetime
from typing import Any

_lazy_cache = {}


def _L(module_path):
    if module_path not in _lazy_cache:
        _lazy_cache[module_path] = _il.import_module(module_path)
    return _lazy_cache[module_path]


# Lightweight imports (eager — these are fast)
from fastapi import APIRouter
from pydantic import BaseModel, Field

from app.cross_chain_correlator import ChainFingerprint, CrossChainCorrelator

router = APIRouter(prefix="/api/v1/security", tags=["security"])

# ─── SINGLETONS ────────────────────────────────────────────

_sentinel_manager = None
_wallet_detector = None
_token_detector = None


async def get_sentinel() -> SentinelManager:
    global _sentinel_manager
    if _sentinel_manager is None:
        _sentinel_manager = _L("app.mempool_sentinel").SentinelManager()
    return _sentinel_manager


async def get_wallet_detector():
    global _wallet_detector
    if _wallet_detector is None:
        _ml = _L("app.ml_anomaly")
        _wallet_detector = _ml.WalletAnomalyDetector(contamination=0.05)
    return _wallet_detector


async def get_token_detector() -> TokenMetricAnomalyDetector:
    global _token_detector
    if _token_detector is None:
        _ml = _L("app.ml_anomaly")
        _token_detector = _ml.TokenMetricAnomalyDetector()
    return _token_detector


# ─── MODELS ────────────────────────────────────────────────


class ContractScanRequest(BaseModel):
    address: str = Field(..., min_length=32, max_length=44)
    chain: str = Field(default="ethereum")
    deep: bool = Field(default=False, description="Run Slither + Mythril deep scan")


class BatchContractRequest(BaseModel):
    addresses: list[str] = Field(..., min_length=1, max_length=50)
    chain: str = Field(default="ethereum")
    deep: bool = Field(default=False)


class WalletCheckRequest(BaseModel):
    address: str = Field(..., min_length=32, max_length=44)
    chain: str = Field(default="auto")


class WalletAnalyzeRequest(BaseModel):
    address: str = Field(..., min_length=32, max_length=44)
    transactions: list[dict[str, Any]] = Field(default_factory=list, description="Raw tx list for ML analysis")
    chain: str = Field(default="auto")


class GraphAnalyzeRequest(BaseModel):
    transactions: list[dict[str, Any]] = Field(..., min_length=2, max_length=1000)
    chain: str = Field(default="ethereum")
    analysis_types: list[str] = Field(default=["clusters", "flows", "anomalies"])


class ThreatBatchRequest(BaseModel):
    addresses: list[str] = Field(..., min_length=1, max_length=100)
    chain: str = Field(default="evm")


class CrossChainRequest(BaseModel):
    fingerprints: list[dict[str, Any]] = Field(..., min_length=2, description="Chain fingerprints")
    chains: list[str] = Field(default=["ethereum", "base", "solana"])


class TokenMetricRequest(BaseModel):
    token: str = Field(...)
    metrics: dict[str, float] = Field(..., description="Current metric values")


class AlertAckRequest(BaseModel):
    alert_id: str = Field(...)


# ─── CONTRACT ANALYSIS ─────────────────────────────────────


@router.get("/contract/{address}")
async def contract_analysis(address: str, chain: str = "ethereum", deep: bool = False):
    """Analyze contract — bytecode scan + optional Slither/Mythril deep scan."""

    # Fast bytecode scan
    _oca = _L("app.onchain_analyzer")
    result = await _oca.analyze_contract(address, chain)
    response = {
        "address": result.address,
        "chain": result.chain,
        "is_contract": result.is_contract,
        "is_proxy": result.is_proxy,
        "proxy_type": result.proxy_type,
        "proxy_implementation": result.proxy_implementation,
        "risk_score": result.risk_score,
        "risk_flags": result.risk_flags,
        "detected_interfaces": result.detected_interfaces,
        "has_selfdestruct": result.has_selfdestruct,
        "has_delegatecall": result.has_delegatecall,
        "has_mint": result.has_mint,
        "has_burn": result.has_burn,
        "has_renounce_ownership": result.has_renounce_ownership,
        "has_reentrancy_guard": result.has_reentrancy_guard,
        "uses_tx_origin": result.uses_tx_origin,
        "uses_timestamp": result.uses_timestamp,
        "honeypot_indicators": result.honeypot_indicators,
        "anti_bot_detected": result.anti_bot_detected,
        "uniswap_pair": result.uniswap_pair,
        "analysis_time": result.analysis_time,
    }

    # Deep scan (if requested)
    if deep:
        try:
            _deep = _L("app.contract_deepscan")
            deep_result = await _deep.deep_scan_contract(address, chain, use_cache=True)
            response["deep_scan"] = deep_result.to_dict()
            # Merge scores
            response["risk_score"] = max(response["risk_score"], deep_result.risk_score)
            response["deep_vulnerabilities"] = len(deep_result.vulnerabilities)
        except Exception as e:
            response["deep_scan_error"] = str(e)

    return response


@router.post("/scan/batch")
async def batch_contract_scan(req: BatchContractRequest):
    """Batch contract scan — fast or deep."""
    if req.deep:
        _deep = _L("app.contract_deepscan")
        results = await _deep.batch_deep_scan(req.addresses, req.chain)
        return {
            "scanned": len(results),
            "chain": req.chain,
            "high_risk": [r.address for r in results if r.risk_score >= 70],
            "critical": [r.address for r in results if r.risk_score >= 90],
            "results": [r.to_dict() for r in results],
        }
    else:
        _oca = _L("app.onchain_analyzer")
        results = await _oca.batch_analyze_contracts(req.addresses, req.chain)
        return {
            "scanned": len(results),
            "chain": req.chain,
            "high_risk": [r.address for r in results if r.risk_score >= 70],
            "proxies": [r.address for r in results if r.is_proxy],
            "honeypots": [r.address for r in results if r.honeypot_indicators],
            "results": [
                {
                    "address": r.address,
                    "risk_score": r.risk_score,
                    "risk_flags": r.risk_flags,
                    "is_proxy": r.is_proxy,
                    "honeypot": r.honeypot_indicators,
                }
                for r in results
            ],
        }


# ─── WALLET / THREAT INTEL ──────────────────────────────────


@router.get("/wallet/{address}")
async def wallet_security_check(address: str, chain: str = "auto"):
    """Full wallet security check: reputation + threat intel."""
    _cg = _L("app.crypto_guard")
    reputation = await _cg.check_wallet_reputation(address, chain)
    _ti = _L("app.threat_intel")
    agg = await _ti.get_aggregator()
    threat_rep = await agg.check_address(address, chain)

    final_score = max(reputation.get("score", 0), threat_rep.risk_score)

    return {
        "address": address,
        "chain": chain,
        "risk_score": final_score,
        "blocked": reputation.get("blocked", False) or threat_rep.is_blocked,
        "sanctioned": threat_rep.is_sanctioned,
        "scam": threat_rep.is_scam,
        "mixer": threat_rep.is_mixer,
        "categories": list(threat_rep.categories),
        "tags": list(threat_rep.tags),
        "threat_reports": len(threat_rep.threat_reports),
        "reputation_flags": reputation.get("flags", []),
        "first_flagged": threat_rep.first_flagged,
        "last_flagged": threat_rep.last_flagged,
    }


@router.post("/wallet/analyze")
async def wallet_ml_analysis(req: WalletAnalyzeRequest):
    """ML behavioral analysis of wallet transactions."""
    from app.ml_anomaly import extract_wallet_features

    features = extract_wallet_features(req.transactions, req.address)
    detector = await get_wallet_detector()

    # Fit detector if we have comparison data
    if req.transactions and len(req.transactions) > 5:
        detector.fit([features])
        result = detector.predict(features)
    else:
        result = {"error": "Insufficient transactions for ML analysis (need >5)"}

    return {
        "address": req.address,
        "features": {
            "tx_count": features.tx_count,
            "avg_tx_value": features.avg_tx_value,
            "unique_counterparties": features.unique_counterparties,
            "dex_swaps": features.dex_swaps,
            "burst_ratio": features.burst_ratio,
            "night_ratio": features.night_tx_ratio,
        },
        "ml_result": result,
    }


@router.post("/threat/check")
async def batch_threat_check(req: ThreatBatchRequest):
    """Batch threat intel lookup."""
    _ti = _L("app.threat_intel")
    agg = await _ti.get_aggregator()
    results = await agg.batch_check(req.addresses, req.chain)

    return {
        "checked": len(results),
        "blocked": [a for a, r in results.items() if r.is_blocked],
        "sanctioned": [a for a, r in results.items() if r.is_sanctioned],
        "scams": [a for a, r in results.items() if r.is_scam],
        "mixers": [a for a, r in results.items() if r.is_mixer],
        "results": {a: r.to_dict() for a, r in results.items()},
    }


# ─── GRAPH ANALYSIS ─────────────────────────────────────────


@router.post("/graph/analyze")
async def graph_analyze(req: GraphAnalyzeRequest):
    """Transaction graph: clusters, flows, anomalies."""
    _tga = _L("app.tx_graph_analyzer")
    graph = await _tga.build_graph_from_transactions(req.transactions, req.chain)

    response = {
        "nodes": len(graph.nodes),
        "edges": len(graph.edges),
        "wallets": list(graph.nodes.keys())[:50],
    }

    if "clusters" in req.analysis_types:
        clusters = graph.cluster_by_louvain()
        response["clusters"] = [
            {
                "id": c.id,
                "wallet_count": len(c.wallets),
                "total_volume": c.total_volume,
            }
            for c in clusters[:10]
        ]

    if "shared_funding" in req.analysis_types:
        shared = graph.cluster_by_shared_funding(min_common=2)
        response["shared_funding_clusters"] = [
            {
                "id": c.id,
                "wallet_count": len(c.wallets),
                "shared_funders": c.shared_funders[:5],
            }
            for c in shared[:10]
        ]

    if "flows" in req.analysis_types:
        cycles = graph.detect_circular_flows(max_cycle_len=4)
        response["circular_flows"] = len(cycles)
        response["wash_trading_suspected"] = len(cycles) > 2

    if "anomalies" in req.analysis_types:
        bursts = graph.detect_burst_transfers(min_count=15)
        sudden = graph.detect_sudden_wealth(multiplier=20.0)
        response["burst_activity"] = bursts[:5]
        response["sudden_wealth"] = sudden[:5]

    if "export" in req.analysis_types:
        import tempfile

        path = tempfile.mktemp(suffix=".gexf")
        graph.export_gexf(path)
        response["gexf_export_path"] = path

    return response


# ─── CROSS-CHAIN ────────────────────────────────────────────


@router.post("/crosschain/correlate")
async def crosschain_correlate(req: CrossChainRequest):
    """Find cross-chain wallet correlations."""
    correlator = CrossChainCorrelator(confidence_threshold=0.6)

    # Add fingerprints
    for fp_data in req.fingerprints:
        import numpy as np

        fp = ChainFingerprint(
            address=fp_data["address"],
            chain=fp_data["chain"],
            feature_vector=np.array(fp_data.get("features", []), dtype=np.float64),
            first_active=fp_data.get("first_active"),
            last_active=fp_data.get("last_active"),
            total_txs=fp_data.get("total_txs", 0),
            total_volume=fp_data.get("total_volume", 0.0),
            known_tags=fp_data.get("tags", []),
        )
        correlator.add_fingerprint(fp)

    # Find links
    links = correlator.find_all_links(req.chains)

    return {
        "fingerprints_analyzed": len(req.fingerprints),
        "links_found": len(links),
        "high_confidence_links": [line.to_dict() for line in links if line.confidence >= 0.8],
        "all_links": [line.to_dict() for line in links[:20]],
    }


# ─── ML / ANOMALY ───────────────────────────────────────────


@router.post("/ml/token/anomaly")
async def token_metric_anomaly(req: TokenMetricRequest):
    """Detect token metric anomalies vs baseline."""
    detector = await get_token_detector()
    detector.update_baseline(req.token, req.metrics)
    result = detector.detect_anomaly(req.token, req.metrics)
    return result


# ─── MEMPOOL ────────────────────────────────────────────────


@router.get("/mempool/status")
async def mempool_status():
    """Mempool sentinel status + recent detections."""
    sentinel = await get_sentinel()
    stats = sentinel.get_stats()

    patterns = sentinel.sentinel.get_recent_patterns(20) if sentinel.sentinel else []

    return {
        **stats,
        "recent_patterns": patterns,
    }


@router.post("/mempool/start")
async def mempool_start(chain: str = "ethereum"):
    sentinel = await get_sentinel()
    await sentinel.start(chain)
    return {"status": "started", "chain": chain}


@router.post("/mempool/stop")
async def mempool_stop():
    sentinel = await get_sentinel()
    await sentinel.stop()
    return {"status": "stopped"}


# ─── DASHBOARD ──────────────────────────────────────────────


@router.get("/dashboard/stats")
async def dashboard_stats():
    """Security dashboard statistics."""
    _sd = _L("app.security_dashboard")
    dash = _sd.get_dashboard()
    return dash.get_stats()


@router.get("/dashboard/alerts")
async def dashboard_alerts(
    severity: str | None = None,
    source: str | None = None,
    limit: int = 100,
    unacknowledged: bool = False,
):
    """Query security alerts."""
    _sd = _L("app.security_dashboard")
    dash = _sd.get_dashboard()
    return dash.get_alerts(severity, source, limit, unacknowledged)


@router.post("/dashboard/acknowledge")
async def dashboard_acknowledge(req: AlertAckRequest):
    """Acknowledge an alert."""
    _sd = _L("app.security_dashboard")
    dash = _sd.get_dashboard()
    success = await dash.acknowledge(req.alert_id)
    return {"acknowledged": success}


@router.get("/dashboard/score")
async def dashboard_score(hours: int = 24):
    """Get rolling security score."""
    _sd = _L("app.security_dashboard")
    dash = _sd.get_dashboard()
    return {
        "security_score": dash.get_security_score(hours),
        "window_hours": hours,
        "max": 100,
    }


@router.get("/dashboard/timeline/{target}")
async def dashboard_timeline(target: str):
    """Get incident timeline for a target."""
    _sd = _L("app.security_dashboard")
    dash = _sd.get_dashboard()
    return {"timeline": dash.get_incident_timeline(target)}


# ─── ENTITY LABELING ─────────────────────────────────────────


@router.get("/entity/label/{address}")
async def entity_label(address: str):
    """Lookup entity label for an address."""
    _el = _L("app.entity_labeler")
    entity = await _el.label_address(address)
    if not entity:
        return {"address": address, "entity": None, "known": False}
    return {"address": address, "entity": entity.to_dict(), "known": True}


@router.post("/entity/batch")
async def entity_batch_label(addresses: list[str]):
    """Batch entity label lookup."""
    _el = _L("app.entity_labeler")
    return await _el.batch_label_addresses(addresses)


@router.get("/entity/search")
async def entity_search(q: str = "", category: str | None = None, limit: int = 50):
    """Search entity database."""
    _el = _L("app.entity_labeler")
    db = await _el.get_entity_labeler()
    results = await db.search_entities(query=q, category=category, limit=limit)
    return {"results": [r.to_dict() for r in results], "count": len(results)}


@router.get("/entity/stats")
async def entity_stats():
    """Entity database statistics."""
    _el = _L("app.entity_labeler")
    db = await _el.get_entity_labeler()
    return await db.get_stats()


@router.get("/entity/{entity_id}")
async def entity_detail(entity_id: str):
    """Full entity graph with addresses and relationships."""
    _el = _L("app.entity_labeler")
    db = await _el.get_entity_labeler()
    return await db.get_entity_graph(entity_id)


# ─── PORTFOLIO TRACKER ──────────────────────────────────────


@router.get("/portfolio/{wallet}")
async def portfolio_single(wallet: str, chain: str = "ethereum"):
    """Get portfolio for a single wallet."""
    _pt = _L("app.portfolio_tracker")
    portfolio = await _pt.get_portfolio(wallet, chain)
    return portfolio.to_dict()


@router.post("/portfolio/multi")
async def portfolio_multi(wallets: list[dict[str, str]]):
    """Aggregate portfolio across multiple wallets/chains."""
    _pt = _L("app.portfolio_tracker")
    result = await _pt.get_multi_wallet_portfolio(wallets)
    return result.to_dict()


@router.get("/portfolio/history/{wallet}")
async def portfolio_history(wallet: str, chain: str = "ethereum", hours: int = 24):
    """Historical net worth chart data."""
    _pt = _L("app.portfolio_tracker")
    history = await _pt.get_portfolio_history(wallet, chain, hours)
    return {
        "wallet": wallet,
        "chain": chain,
        "hours": hours,
        "data_points": len(history),
        "history": history,
    }


# ─── WALLET MONITOR ─────────────────────────────────────────


@router.post("/monitor/watch")
async def monitor_watch(address: str, chain: str = "ethereum", tags: str = ""):
    """Add a wallet to the watchlist."""
    _wm = _L("app.wallet_monitor")
    manager = _wm.WalletMonitorManager()
    await manager.add_watch(address, chain, tags.split(",") if tags else [])
    return {"watched": True, "address": address, "chain": chain}


@router.post("/monitor/unwatch")
async def monitor_unwatch(address: str, chain: str = "ethereum"):
    """Remove a wallet from the watchlist."""
    _wm = _L("app.wallet_monitor")
    manager = _wm.WalletMonitorManager()
    await manager.remove_watch(address, chain)
    return {"unwatched": True, "address": address, "chain": chain}


@router.get("/monitor/watches")
async def monitor_list():
    """List all watched wallets."""
    _wm = _L("app.wallet_monitor")
    manager = _wm.WalletMonitorManager()
    watches = await manager.list_watches()
    return {"watches": watches, "count": len(watches)}


# ─── EXCHANGE FLOW ANALYZER ─────────────────────────────────


@router.post("/flows/analyze")
async def flows_analyze(
    entity_addresses: list[str],
    entity_name: str,
    entity_id: str,
    chain: str,
    transactions: list[dict[str, Any]],
    hours: int = 24,
):
    """Analyze fund flows for an entity."""
    _efa = _L("app.exchange_flow_analyzer")
    return await _efa.analyze_entity_flows(entity_addresses, entity_name, entity_id, chain, transactions, hours)


@router.post("/flows/reserves")
async def flows_reserves(
    entity_addresses: list[str],
    entity_name: str,
    entity_id: str,
    chain: str,
):
    """Get reserve snapshot for entity addresses."""
    _efa = _L("app.exchange_flow_analyzer")
    return await _efa.get_reserves(entity_addresses, entity_name, entity_id, chain)


@router.post("/flows/compare")
async def flows_compare(
    entity_a: dict[str, Any],
    entity_b: dict[str, Any],
    transactions: list[dict[str, Any]],
):
    """Compare fund flows between two entities."""
    _efa = _L("app.exchange_flow_analyzer")
    return await _efa.compare_flows(entity_a, entity_b, transactions)


# ─── HEALTH ────────────────────────────────────────────────


@router.get("/health")
async def security_health():
    """Full subsystem health check."""

    # Check all modules
    modules = {}

    try:
        from app.onchain_analyzer import get_w3

        w3 = get_w3("ethereum")
        modules["onchain_analyzer"] = "connected" if w3 and w3.is_connected() else "disconnected"
    except Exception as e:
        modules["onchain_analyzer"] = f"error: {str(e)[:30]}"

    try:
        import sklearn

        modules["ml_anomaly"] = f"sklearn {sklearn.__version__}"
    except ImportError:
        modules["ml_anomaly"] = "sklearn not installed"

    try:
        import slither_analyzer

        modules["contract_deepscan"] = f"slither {slither_analyzer.__version__}"
    except ImportError:
        modules["contract_deepscan"] = "slither not installed"

    # Redis
    try:
        r = await _get_redis()
        modules["redis"] = "connected" if r and await r.ping() else "disconnected"
    except Exception:
        modules["redis"] = "disconnected"

    return {
        "status": "operational",
        "modules": modules,
        "version": "2.0.0",
        "timestamp": datetime.now(UTC).isoformat(),
    }


# Redis helper for health check
async def _get_redis():
    try:
        import redis.asyncio as redis_async

        return redis_async.Redis(
            host=os.getenv("REDIS_HOST", "127.0.0.1"),
            port=int(os.getenv("REDIS_PORT", "6379")),
            password=os.getenv("REDIS_PASSWORD") or None,
            db=int(os.getenv("REDIS_DB", "0")),
            decode_responses=True,
        )
    except Exception:
        return None
