#!/usr/bin/env python3
"""#18 — Rug Recovery Indexer. When a rug is detected by SENTINEL, tracks all outbound
txs from deployer. Builds trace graph of stolen funds. Premium: notify affected addresses."""

import os
from typing import Any

import httpx
from fastapi import APIRouter, Query
from pydantic import BaseModel

router = APIRouter(prefix="/api/v1/rug-recovery", tags=["rug-recovery"])

BACKEND = os.environ.get("BACKEND_URL", "http://localhost:8000")
DATABUS = os.environ.get("DATABUS_URL", "http://localhost:8000/api/v1/databus")


class RugTraceResult(BaseModel):
    rug_token: str
    chain: str
    deployer: str
    total_stolen_usd: float
    fund_hops: int
    destination_addresses: list[dict]
    cex_deposits: list[dict]
    mixer_usage: list[dict]
    recovery_possible: bool
    trace_graph: dict[str, Any]


async def _trace_outbound_txs(address: str, chain: str, max_hops: int = 3) -> list[dict]:
    """Trace outbound transactions from an address across hops."""
    txs: list[dict] = []
    try:
        async with httpx.AsyncClient(timeout=10) as client:
            resp = await client.get(
                f"{DATABUS}/{chain}/transactions/{address}", params={"direction": "out", "limit": 50}
            )
            if resp.status_code == 200:
                data = resp.json().get("data", {})
                txs = data.get("transactions", [])[:50]
    except Exception:
        pass
    return txs


async def _check_cex_deposit(address: str, chain: str) -> dict | None:
    """Check if an address is a known CEX deposit address."""
    try:
        async with httpx.AsyncClient(timeout=5) as client:
            resp = await client.get(f"{BACKEND}/api/v1/eth-labels/lookup/{address}")
            if resp.status_code == 200:
                for label in resp.json().get("labels", []):
                    lbl = (label.get("label", "") or "").lower()
                    if any(w in lbl for w in ["exchange", "cex", "binance", "coinbase", "kraken", "okx", "bybit"]):
                        return {"address": address, "cex": label.get("label"), "chain": chain}
    except Exception:
        pass
    return None


async def _check_mixer(address: str, chain: str) -> dict | None:
    """Check if an address is a known mixer/tumbler."""
    try:
        async with httpx.AsyncClient(timeout=5) as client:
            resp = await client.get(f"{BACKEND}/api/v1/eth-labels/lookup/{address}")
            if resp.status_code == 200:
                for label in resp.json().get("labels", []):
                    lbl = (label.get("label", "") or "").lower()
                    if any(w in lbl for w in ["mixer", "tornado", "tumbler"]):
                        return {"address": address, "mixer": label.get("label"), "chain": chain}
    except Exception:
        pass
    return None


@router.get("/trace/{token_address}")
async def trace_rug(token_address: str, chain: str = Query("ethereum"), max_hops: int = Query(3, le=5)):
    """Trace stolen funds from a rugged token."""
    # Step 1: Get deployer from SENTINEL
    deployer = ""
    total_stolen = 0.0
    try:
        async with httpx.AsyncClient(timeout=15) as client:
            resp = await client.post(
                f"{BACKEND}/api/v1/token/scan",
                json={"token_address": token_address, "chain": chain},
                headers={"X-RMI-Key": "rmi-internal-2026"},
            )
            if resp.status_code == 200:
                scan = resp.json()
                deployer = scan.get("pro", {}).get("deployer_address", "")
                total_stolen = scan.get("free", {}).get("liquidity_usd", 0) or 0
    except Exception:
        pass

    if not deployer:
        return {"error": "Could not identify deployer", "token_address": token_address, "chain": chain}

    # Step 2: Trace outbound txs from deployer
    direct_txs = await _trace_outbound_txs(deployer, chain)

    # Step 3: Check destinations for CEX/mixer involvement
    destinations = set()
    for tx in direct_txs:
        to_addr = tx.get("to", "")
        if to_addr:
            destinations.add(to_addr)

    cex_hits: list[dict] = []
    mixer_hits: list[dict] = []
    for addr in list(destinations)[:20]:
        cex = await _check_cex_deposit(addr, chain)
        if cex:
            cex_hits.append(cex)
        mixer = await _check_mixer(addr, chain)
        if mixer:
            mixer_hits.append(mixer)

    recovery_possible = len(cex_hits) > 0

    # Build trace graph
    nodes = [{"id": deployer[:16], "label": f"Deployer {deployer[:10]}", "type": "deployer"}]
    edges: list[dict] = []
    for tx in direct_txs[:20]:
        to_id = tx.get("to", "")[:16]
        if to_id:
            nodes.append({"id": to_id, "label": f"Wallet {to_id}", "type": "wallet"})
            edges.append(
                {
                    "source": deployer[:16],
                    "target": to_id,
                    "amount_usd": float(tx.get("value_usd", 0) or 0),
                    "tx_hash": tx.get("hash", "?")[:16],
                }
            )

    return {
        "rug_token": token_address,
        "chain": chain,
        "deployer": deployer,
        "total_stolen_usd": round(total_stolen, 2),
        "fund_hops": 1,
        "destination_addresses": [
            {"address": a, "tx_count": sum(1 for tx in direct_txs if tx.get("to") == a)}
            for a in list(destinations)[:10]
        ],
        "cex_deposits": cex_hits,
        "mixer_usage": mixer_hits,
        "recovery_possible": recovery_possible,
        "trace_graph": {"nodes": nodes, "edges": edges},
        "recommendation": (
            "Funds traced to known CEX addresses. Contact exchange with tx hashes and police report."
            if recovery_possible
            else "No CEX deposits detected yet. Monitor deployer wallet for future CEX transfers."
        ),
    }


@router.get("/victims/{token_address}")
async def get_affected_victims(token_address: str, chain: str = Query("ethereum")):
    """Get list of addresses that interacted with (lost money to) a rug token."""
    victims: list[dict] = []
    try:
        async with httpx.AsyncClient(timeout=10) as client:
            resp = await client.get(f"{DATABUS}/{chain}/token/{token_address}/holders")
            if resp.status_code == 200:
                data = resp.json().get("data", {})
                holders = data.get("holders", [])
                for h in holders[:50]:
                    if float(h.get("pnl_usd", 0) or 0) < -1:
                        victims.append(
                            {
                                "address": h.get("address", "")[:20] + "...",
                                "loss_usd": round(abs(float(h.get("pnl_usd", 0) or 0)), 2),
                                "tokens_held": h.get("balance", 0),
                            }
                        )
    except Exception:
        pass

    victims.sort(key=lambda v: v["loss_usd"], reverse=True)
    return {
        "token_address": token_address,
        "chain": chain,
        "victim_count": len(victims),
        "total_loss_usd": round(sum(v["loss_usd"] for v in victims), 2),
        "victims": victims[:20],
    }
