"""
SwarmPay REST API Server.

Damit können ALLE AI-Agents (OpenAI, LangChain, CrewAI, Custom)
über HTTP auf SwarmPay zugreifen.

Starte mit:
    python3 -m swarmpay.api_server

Endpoints:
    POST /wallets              → Wallet erstellen
    GET  /wallets/:id/balance  → Kontostand
    POST /pay                  → Zahlung senden
    GET  /services             → Alle Services listen
    GET  /services/:type       → Service suchen
    POST /services             → Service registrieren
    POST /buy                  → Service kaufen (Suche + Zahlung)
    GET  /fees                 → Gebührenstruktur
"""
from __future__ import annotations

import hashlib
import json
import math
import os
import secrets
import sys
import time
from collections import defaultdict
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs

from swarmpay import __version__
from swarmpay.client import SwarmPay
from swarmpay.dashboard import build_dashboard_feed, build_dashboard_summary
from swarmpay.onchain_escrow import OnchainEscrowConfig

# ── Rate Limiting ─────────────────────────────────────────────
_RATE_LIMIT_REQUESTS = int(os.getenv("SWARMPAY_RATE_LIMIT", "60"))  # per minute
_RATE_LIMIT_WINDOW = 60  # seconds
_request_log: dict[str, list[float]] = defaultdict(list)


def _check_rate_limit(client_ip: str) -> bool:
    """Returns True if request is allowed, False if rate limited."""
    now = time.time()
    cutoff = now - _RATE_LIMIT_WINDOW
    # Clean old entries
    _request_log[client_ip] = [t for t in _request_log[client_ip] if t > cutoff]
    if len(_request_log[client_ip]) >= _RATE_LIMIT_REQUESTS:
        return False
    _request_log[client_ip].append(now)
    return True


# ── API Key Management ────────────────────────────────────────
_API_KEY = os.getenv("SWARMPAY_API_KEY", "")

def _generate_api_key() -> str:
    """Generate a new API key if none is set."""
    return "sk_swarmpay_" + secrets.token_hex(24)

def _init_api_key() -> str:
    """Initialize API key from env or generate one."""
    global _API_KEY
    if not _API_KEY:
        _API_KEY = _generate_api_key()
        print(f"\n  ⚠ No SWARMPAY_API_KEY set. Generated temporary key:")
        print(f"    {_API_KEY}")
        print(f"    Set SWARMPAY_API_KEY in .env for persistence.\n")
    return _API_KEY

# ── CORS Configuration ───────────────────────────────────────
_ALLOWED_ORIGINS = os.getenv("SWARMPAY_CORS_ORIGINS", "").split(",")


def _build_onchain_escrow_config() -> OnchainEscrowConfig | None:
    contract_address = os.getenv("SWARMPAY_ESCROW_CONTRACT_ADDRESS", "")
    token_address = os.getenv("SWARMPAY_ESCROW_TOKEN_ADDRESS", "")
    fee_recipient = os.getenv("SWARMPAY_ESCROW_FEE_RECIPIENT", "") or os.getenv("SWARMPAY_ADMIN_WALLET", "")
    dispute_resolver = os.getenv("SWARMPAY_ESCROW_DISPUTE_RESOLVER", "")
    chain_id_raw = os.getenv("SWARMPAY_CHAIN_ID", "")
    if not all([contract_address, token_address, fee_recipient, dispute_resolver, chain_id_raw]):
        return None
    return OnchainEscrowConfig(
        chain_id=int(chain_id_raw),
        contract_address=contract_address,
        token_address=token_address,
        fee_recipient=fee_recipient,
        dispute_resolver=dispute_resolver,
    )


def _serialize_webhook_event(event) -> dict:
    return {
        "event_id": event.event_id,
        "event_type": event.event_type,
        "created_at": event.created_at,
        "payload": event.payload,
    }


def _serialize_subscription(subscription) -> dict:
    return {
        "subscription_id": subscription.subscription_id,
        "target_url": subscription.target_url,
        "event_types": list(subscription.event_types),
        "enabled": subscription.enabled,
        "max_retries": subscription.max_retries,
    }


def _serialize_delivery(delivery) -> dict:
    return {
        "subscription_id": delivery.subscription_id,
        "event_id": delivery.event_id,
        "attempt": delivery.attempt,
        "success": delivery.success,
        "status_code": delivery.status_code,
        "error": delivery.error,
        "next_attempt_at": delivery.next_attempt_at,
        "delivered_at": delivery.delivered_at,
    }


def _perform_onchain_action(swarmpay: SwarmPay, wallet, action: str, escrow_nonce: int, body: dict) -> dict:
    if action == "confirm":
        result = swarmpay.confirm_onchain_delivery(
            wallet,
            escrow_nonce=escrow_nonce,
            escrow_id=body.get("escrow_id", ""),
        )
    elif action == "release":
        result = swarmpay.release_onchain_escrow(
            wallet,
            escrow_nonce=escrow_nonce,
            escrow_id=body.get("escrow_id", ""),
        )
    elif action == "refund":
        result = swarmpay.refund_onchain_escrow(
            wallet,
            escrow_nonce=escrow_nonce,
            escrow_id=body.get("escrow_id", ""),
        )
    elif action == "dispute":
        result = swarmpay.dispute_onchain_escrow(
            wallet,
            escrow_nonce=escrow_nonce,
            escrow_id=body.get("escrow_id", ""),
            reason=body.get("reason", ""),
        )
    else:
        raise ValueError(f"Unsupported action: {action}")
    return {
        "escrow_id": result.escrow_id,
        "escrow_nonce": result.escrow_nonce,
        "tx_hash": result.tx_hash,
        "status": result.status.value,
        "reason_hash": result.reason_hash,
    }

# ── SwarmPay Instanz ─────────────────────────────────────────
_swarmpay = SwarmPay(
    admin_wallet=os.getenv("SWARMPAY_ADMIN_WALLET", "") or os.getenv("PAYFI_ADMIN_WALLET", ""),
    fee_percent=float(os.getenv("SWARMPAY_FEE_PERCENT", "0.01")),
    network=os.getenv("SWARMPAY_NETWORK", "base-sepolia"),
    onchain_escrow_config=_build_onchain_escrow_config(),
)
_wallets: dict = {}


class SwarmPayHandler(BaseHTTPRequestHandler):
    """HTTP Request Handler für die SwarmPay API."""

    def _check_rate_limit(self) -> bool:
        """Check if client is rate limited."""
        client_ip = self.client_address[0]
        if not _check_rate_limit(client_ip):
            self._send_json({"error": "Rate limit exceeded. Try again later."}, 429)
            return False
        return True

    def _check_auth(self) -> bool:
        """Validate API key from Authorization header."""
        auth_header = self.headers.get("Authorization", "")
        if auth_header.startswith("Bearer "):
            token = auth_header[7:]
            # Constant-time comparison to prevent timing attacks
            return secrets.compare_digest(token, _API_KEY)
        return False

    def _send_json(self, data: dict, status: int = 200):
        self.send_response(status)
        self.send_header("Content-Type", "application/json")

        # CORS: Only allow configured origins
        origin = self.headers.get("Origin", "")
        if _ALLOWED_ORIGINS and _ALLOWED_ORIGINS != [""]:
            if origin in _ALLOWED_ORIGINS:
                self.send_header("Access-Control-Allow-Origin", origin)
        # If no origins configured, no CORS header = same-origin only

        self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
        self.send_header("Access-Control-Allow-Headers", "Content-Type, Authorization")
        self.end_headers()
        self.wfile.write(json.dumps(data, indent=2).encode())

    def _read_body(self) -> dict:
        length = int(self.headers.get("Content-Length", 0))
        if length == 0:
            return {}
        body = self.rfile.read(length)
        return json.loads(body)

    def _validate_amount(self, amount) -> tuple[float | None, str]:
        """Validate and parse amount. Returns (value, error)."""
        try:
            value = float(amount)
        except (ValueError, TypeError):
            return None, "Invalid amount format"
        if not math.isfinite(value):
            return None, "Amount must be a finite number"
        if value <= 0:
            return None, "Amount must be greater than 0"
        return value, ""

    def do_OPTIONS(self):
        self._send_json({})

    def do_GET(self):
        if not self._check_rate_limit():
            return
        path = urlparse(self.path).path.rstrip("/")

        # GET / → API Info (no auth required)
        if path == "" or path == "/":
            self._send_json({
                "name": "SwarmPay API",
                "version": __version__,
                "description": "The Payment Layer for AI Agents",
                "endpoints": {
                    "POST /wallets": "Create wallet",
                    "GET /wallets/:id/balance": "Check balance",
                    "POST /pay": "Send payment",
                    "GET /services": "List services",
                    "GET /services/:type": "Find service",
                    "POST /services": "Register service",
                    "POST /buy": "Buy service",
                    "POST /escrows/onchain": "Create on-chain escrow",
                    "POST /escrows/onchain/actions": "Run on-chain escrow action",
                    "GET /events": "List emitted events",
                    "POST /webhooks": "Register webhook target",
                    "GET /webhooks": "List webhook targets",
                    "GET /deliveries": "List webhook deliveries",
                    "POST /webhooks/retry": "Retry due webhook deliveries",
                    "GET /dashboard/summary": "Dashboard metrics",
                    "GET /dashboard/feed": "Dashboard activity feed",
                    "GET /fees": "Fee structure",
                },
            })
            return

        # All other GET endpoints require auth
        if not self._check_auth():
            self._send_json({"error": "Unauthorized. Provide API key via Authorization: Bearer <key>"}, 401)
            return

        # GET /services → Alle Services
        if path == "/services":
            services = _swarmpay.registry.list_all()
            self._send_json({"services": services, "count": len(services)})

        # GET /services/:type → Service suchen
        elif path.startswith("/services/"):
            service_type = path.split("/services/")[1]
            services = _swarmpay.find_service(service_type)
            self._send_json({"services": services, "count": len(services)})

        # GET /wallets/:id/balance → Balance
        elif path.startswith("/wallets/") and path.endswith("/balance"):
            agent_id = path.split("/wallets/")[1].replace("/balance", "")
            if agent_id not in _wallets:
                self._send_json({"error": "Agent not found"}, 404)
                return
            wallet = _wallets[agent_id]
            self._send_json({
                "agent_id": agent_id,
                "balance_usdc": wallet.balance("USDC"),
                "wallet_address": wallet.address,
            })

        # GET /fees → Gebührenstruktur
        elif path == "/fees":
            self._send_json({"fee_tiers": _swarmpay.fee_schedule})

        elif path == "/events":
            self._send_json({
                "events": [_serialize_webhook_event(event) for event in _swarmpay.webhook_dispatcher.events],
                "count": len(_swarmpay.webhook_dispatcher.events),
            })

        elif path == "/webhooks":
            self._send_json({
                "subscriptions": [
                    _serialize_subscription(subscription)
                    for subscription in _swarmpay.webhook_dispatcher.subscriptions
                ],
                "count": len(_swarmpay.webhook_dispatcher.subscriptions),
            })

        elif path == "/deliveries":
            self._send_json({
                "deliveries": [
                    _serialize_delivery(delivery)
                    for delivery in _swarmpay.webhook_dispatcher.deliveries
                ],
                "count": len(_swarmpay.webhook_dispatcher.deliveries),
            })

        elif path == "/dashboard/summary":
            self._send_json(build_dashboard_summary(_swarmpay.webhook_dispatcher))

        elif path == "/dashboard/feed":
            query = parse_qs(urlparse(self.path).query)
            limit_raw = query.get("limit", ["20"])[0]
            try:
                limit = max(0, int(limit_raw))
            except ValueError:
                self._send_json({"error": "limit must be an integer"}, 400)
                return
            feed = build_dashboard_feed(_swarmpay.webhook_dispatcher, limit=limit)
            self._send_json({"items": feed, "count": len(feed)})

        else:
            self._send_json({"error": "Endpoint not found"}, 404)

    def do_POST(self):
        if not self._check_rate_limit():
            return
        # All POST endpoints require auth
        if not self._check_auth():
            self._send_json({"error": "Unauthorized. Provide API key via Authorization: Bearer <key>"}, 401)
            return

        path = urlparse(self.path).path.rstrip("/")
        body = self._read_body()

        # POST /wallets → Wallet erstellen
        if path == "/wallets":
            agent_id = body.get("agent_id")
            if not agent_id:
                self._send_json({"error": "agent_id is required"}, 400)
                return
            wallet = _swarmpay.create_wallet(agent_id)
            _wallets[agent_id] = wallet
            self._send_json({
                "agent_id": agent_id,
                "wallet_address": wallet.address,
                "network": wallet.network,
            }, 201)

        # POST /pay → Zahlung
        elif path == "/pay":
            agent_id = body.get("agent_id")
            recipient = body.get("recipient")
            amount_raw = body.get("amount")
            if not all([agent_id, recipient, amount_raw]):
                self._send_json({"error": "agent_id, recipient, amount required"}, 400)
                return
            if agent_id not in _wallets:
                self._send_json({"error": "Agent not found"}, 404)
                return
            amount, error = self._validate_amount(amount_raw)
            if error:
                self._send_json({"error": error}, 400)
                return
            wallet = _wallets[agent_id]
            result = wallet.pay(recipient, amount)
            self._send_json({
                "success": result.success,
                "transaction_id": result.transaction_id,
                "amount_usd": result.amount_usd,
                "fee_usd": result.fee_usd,
                "service_amount_usd": result.service_amount_usd,
                "explorer_url": result.explorer_url,
                "error": result.error,
            })

        # POST /services → Service registrieren
        elif path == "/services":
            agent_id = body.get("agent_id")
            service_type = body.get("service_type")
            price_raw = body.get("price_usd")
            if not all([agent_id, service_type, price_raw]):
                self._send_json({"error": "agent_id, service_type, price_usd required"}, 400)
                return
            if agent_id not in _wallets:
                self._send_json({"error": "Agent not found"}, 404)
                return
            price, error = self._validate_amount(price_raw)
            if error:
                self._send_json({"error": f"Invalid price: {error}"}, 400)
                return
            wallet = _wallets[agent_id]
            service_id = _swarmpay.register_service(
                wallet=wallet,
                service_type=service_type,
                price_usd=price,
                description=body.get("description", ""),
            )
            self._send_json({"service_id": service_id, "status": "registered"}, 201)

        # POST /buy → Service kaufen
        elif path == "/buy":
            agent_id = body.get("agent_id")
            service_type = body.get("service_type")
            if not all([agent_id, service_type]):
                self._send_json({"error": "agent_id, service_type required"}, 400)
                return
            if agent_id not in _wallets:
                self._send_json({"error": "Agent not found"}, 404)
                return
            wallet = _wallets[agent_id]
            result = _swarmpay.buy_service(wallet, service_type)
            if "error" in result:
                self._send_json(result, 404)
                return
            payment = result["payment"]
            self._send_json({
                "success": payment.success,
                "provider": result["provider"],
                "amount_usd": payment.amount_usd,
                "fee_usd": payment.fee_usd,
                "transaction_id": payment.transaction_id,
                "error": payment.error,
            })

        elif path == "/webhooks":
            target_url = body.get("target_url")
            secret = body.get("secret")
            event_types = body.get("event_types", [])
            if not target_url or not secret:
                self._send_json({"error": "target_url and secret required"}, 400)
                return
            subscription = _swarmpay.webhook_dispatcher.subscribe(
                target_url=target_url,
                secret=secret,
                event_types=event_types,
            )
            self._send_json({
                "subscription_id": subscription.subscription_id,
                "target_url": subscription.target_url,
                "event_types": list(subscription.event_types),
                "enabled": subscription.enabled,
            }, 201)

        elif path == "/webhooks/retry":
            retried = _swarmpay.webhook_dispatcher.retry_pending(
                now=float(body.get("now", time.time()))
            )
            self._send_json({
                "deliveries": [_serialize_delivery(delivery) for delivery in retried],
                "count": len(retried),
            })

        elif path == "/escrows/onchain":
            if not _swarmpay.onchain_escrow:
                self._send_json({"error": "On-chain escrow not configured"}, 400)
                return
            agent_id = body.get("agent_id")
            escrow_nonce = body.get("escrow_nonce")
            if not agent_id:
                self._send_json({"error": "agent_id required"}, 400)
                return
            if agent_id not in _wallets:
                self._send_json({"error": "Agent not found"}, 404)
                return
            if escrow_nonce is None:
                self._send_json({"error": "escrow_nonce required"}, 400)
                return
            wallet = _wallets[agent_id]

            if body.get("service_type") and not body.get("provider_wallet"):
                result = _swarmpay.buy_service_onchain_escrow(
                    wallet,
                    body["service_type"],
                    escrow_nonce=int(escrow_nonce),
                    timeout_seconds=int(body.get("timeout_seconds", 3600)),
                    salt=body.get("salt", ""),
                )
                if "error" in result:
                    self._send_json(result, 404)
                    return
                flow = result["transactions"]
                escrow = result["escrow"]
                self._send_json({
                    "escrow_id": escrow.escrow_id,
                    "provider": result["provider"],
                    "approval_tx_hash": flow.approval_tx_hash,
                    "funding_tx_hash": flow.funding_tx_hash,
                    "status": escrow.status.value,
                }, 201)
                return

            provider_wallet = body.get("provider_wallet")
            amount_raw = body.get("amount")
            service_type = body.get("service_type")
            if not all([provider_wallet, amount_raw, service_type]):
                self._send_json({"error": "provider_wallet, amount, service_type required"}, 400)
                return
            amount, error = self._validate_amount(amount_raw)
            if error:
                self._send_json({"error": error}, 400)
                return
            flow = _swarmpay.create_onchain_escrow(
                buyer_wallet=wallet,
                provider_wallet=provider_wallet,
                amount_usd=amount,
                service_type=service_type,
                escrow_nonce=int(escrow_nonce),
                timeout_seconds=int(body.get("timeout_seconds", 3600)),
                reference=body.get("reference", ""),
                salt=body.get("salt", ""),
            )
            self._send_json({
                "escrow_id": flow.escrow.escrow_id,
                "approval_tx_hash": flow.approval_tx_hash,
                "funding_tx_hash": flow.funding_tx_hash,
                "status": flow.escrow.status.value,
            }, 201)

        elif path == "/escrows/onchain/actions":
            if not _swarmpay.onchain_escrow:
                self._send_json({"error": "On-chain escrow not configured"}, 400)
                return
            agent_id = body.get("agent_id")
            action = body.get("action")
            escrow_nonce = body.get("escrow_nonce")
            if not all([agent_id, action]) or escrow_nonce is None:
                self._send_json({"error": "agent_id, action, escrow_nonce required"}, 400)
                return
            if agent_id not in _wallets:
                self._send_json({"error": "Agent not found"}, 404)
                return
            try:
                result = _perform_onchain_action(
                    _swarmpay,
                    _wallets[agent_id],
                    action=action,
                    escrow_nonce=int(escrow_nonce),
                    body=body,
                )
            except ValueError as exc:
                self._send_json({"error": str(exc)}, 400)
                return
            self._send_json(result, 200)

        else:
            self._send_json({"error": "Endpoint not found"}, 404)

    def log_message(self, format, *args):
        """Custom log format."""
        sys.stderr.write(f"[SwarmPay API] {args[0]}\n")


def main():
    api_key = _init_api_key()
    host = os.getenv("SWARMPAY_HOST", "127.0.0.1")
    port = int(os.getenv("SWARMPAY_PORT", "8420"))
    server = HTTPServer((host, port), SwarmPayHandler)

    print()
    print("╔══════════════════════════════════════════════════════════╗")
    print("║         SwarmPay REST API Server                        ║")
    print("║         The Payment Layer for AI Agents                 ║")
    print(f"║         http://{host}:{port}                        ║")
    print("╚══════════════════════════════════════════════════════════╝")
    print()
    print(f"  Auth: Bearer token required for all endpoints")
    print(f"  Host: {host} (set SWARMPAY_HOST to change)")
    print()
    print(f"  Endpoints:")
    print(f"    POST /wallets              Create wallet")
    print(f"    GET  /wallets/:id/balance  Check balance")
    print(f"    POST /pay                  Send payment")
    print(f"    GET  /services             List services")
    print(f"    GET  /services/:type       Find service")
    print(f"    POST /services             Register service")
    print(f"    POST /buy                  Buy service")
    print(f"    GET  /fees                 Fee structure")
    print()
    print(f"  Press Ctrl+C to stop")
    print()

    try:
        server.serve_forever()
    except KeyboardInterrupt:
        print("\n  Server stopped.")
        server.server_close()


if __name__ == "__main__":
    main()
