"""
RMI Freemium Scan Rate Limiter
===============================
- 5 free token/wallet scans per day per identity (IP for anonymous, user ID for auth)
- Premium users: unlimited or per-tier limits
- Internal/cron API key: unlimited bypass
- x402 paid calls: bypass (already paid)
- Returns clear upsell messaging when limit is hit
"""

import logging
import os
from datetime import UTC, datetime, timedelta
from typing import Any

import redis

logger = logging.getLogger(__name__)

# ── Configuration ──

FREE_DAILY_LIMIT = int(os.getenv("FREE_SCAN_LIMIT", "5"))
PRO_DAILY_LIMIT = int(os.getenv("PRO_SCAN_LIMIT", "100"))
ELITE_DAILY_LIMIT = int(os.getenv("ELITE_SCAN_LIMIT", "0"))  # 0 = unlimited

INTERNAL_API_KEY = os.getenv("RMI_INTERNAL_KEY", "rmi-internal-2026")

# Redis setup
REDIS_HOST = os.getenv("REDIS_HOST", "localhost")
REDIS_PORT = int(os.getenv("REDIS_PORT", "6379"))
REDIS_PASSWORD = os.getenv("REDIS_PASSWORD", "")
REDIS_DB = os.getenv("REDIS_USAGE_DB", "2")  # Separate DB for usage tracking

# Human subscriptions (monthly, site access)
SCAN_PACKS = {
    "starter_monthly": {
        "scans": 50,
        "price_usd": 29.99,
        "price_atoms": "29990000",
        "description": "50 scans/day - Starter tier",
    },
    "pro_monthly": {
        "scans": 200,
        "price_usd": 49.99,
        "price_atoms": "49990000",
        "description": "200 scans/day - Pro tier",
    },
    "elite_monthly": {
        "scans": 1000,
        "price_usd": 89.99,
        "price_atoms": "89990000",
        "description": "1000 scans/day + API access - Elite tier",
    },
}

# Bot/API prepaid packs (one-time, no expiry, for AI agents and trading bots)
BOT_PACKS = {
    "bot_scout": {
        "calls": 200,
        "price_usd": 9.99,
        "price_atoms": "9990000",
        "description": "200 API calls - Scout pack for testing bots",
    },
    "bot_hunter": {
        "calls": 1500,
        "price_usd": 49.99,
        "price_atoms": "49990000",
        "description": "1,500 API calls - Hunter pack for active bots (33% savings)",
    },
    "bot_whale": {
        "calls": 10000,
        "price_usd": 199.99,
        "price_atoms": "199990000",
        "description": "10,000 API calls - Whale pack for production bots (60% savings)",
    },
    "bot_api_plan": {
        "calls": 50000,
        "price_usd": 499.00,
        "price_atoms": "499000000",
        "description": "50,000 calls/month - API Plan for SaaS/funds ($0.01/call)",
    },
}

# Trial abuse prevention
TRIAL_CONFIG = {
    "max_free_per_fingerprint": 3,  # 3 free calls per device fingerprint per tool
    "fingerprint_window_hours": 168,  # 7-day fingerprint window
    "max_free_per_ip": 15,  # 15 total free calls per IP across all tools
    "ip_window_hours": 24,  # 24-hour IP window
    "cooldown_after_exhaust": 72,  # 72-hour cooldown after exhausting all trials
}

PREMIUM_UPSELL = {
    "free_limit_reached": {
        "message": f"You've used your {FREE_DAILY_LIMIT} free scans for today. Unlock Starter (50/day), Pro (200/day), or Elite (1000/day + API). $CRM/$CRYPTORUGMUNCH holders get 50% off for 3 months.",
        "upgrade_url": "https://rugmunch.io/pricing",
        "tiers": {
            "free": {
                "daily_limit": FREE_DAILY_LIMIT,
                "price": "$0",
                "features": ["5 scans/day", "Basic safety score", "DexScreener data"],
            },
            "starter": {
                "daily_limit": 50,
                "price": "$29.99/mo",
                "features": [
                    "50 scans/day",
                    "Full risk analysis",
                    "Mint/freeze authority",
                    "Honeypot detection",
                    "LP verification",
                ],
            },
            "pro": {
                "daily_limit": 200,
                "price": "$49.99/mo",
                "features": [
                    "200 scans/day",
                    "All Starter features",
                    "Bundle detection",
                    "MEV analysis",
                    "Whale tracking",
                    "Dev reputation",
                ],
            },
            "elite": {
                "daily_limit": 1000,
                "price": "$89.99/mo",
                "features": [
                    "1000 scans/day",
                    "All Pro features",
                    "Full API access",
                    "Priority support",
                    "SENTINEL deep modules",
                ],
            },
            "enterprise": {
                "daily_limit": "unlimited",
                "price": "Custom",
                "features": [
                    "Unlimited API access",
                    "Dedicated support",
                    "Custom integrations",
                    "SLA guarantee",
                    "Contact: biz@rugmunch.io",
                ],
            },
            "holder_discount": {
                "discount": "50%",
                "duration": "3 months",
                "eligibility": "$CRM and $CRYPTORUGMUNCH holders",
                "verification": "v2 airdrop checker",
                "note": "Verify token holdings via the airdrop checker to claim your 50% discount code",
            },
            "scan_packs": {
                k: {
                    "price": v["price_usd"],
                    "scans": v["scans"] or "unlimited",
                    "description": v["description"],
                }
                for k, v in SCAN_PACKS.items()
            },
        },
    }
}


def _get_redis() -> redis.Redis:
    """Get Redis connection for usage tracking."""
    return redis.Redis(
        host=REDIS_HOST,
        port=REDIS_PORT,
        password=REDIS_PASSWORD,
        db=int(REDIS_DB),
        decode_responses=True,
    )


def _today_key() -> str:
    """Return today's date key for Redis."""
    return datetime.now(UTC).strftime("%Y-%m-%d")


def _identity_key(identity_type: str, identity: str) -> str:
    """Build Redis key for an identity's daily usage."""
    return f"rmi:scan_usage:{_today_key()}:{identity_type}:{identity}"


def check_scan_limit(
    identity: str,
    identity_type: str = "ip",
    tier: str = "free",
    is_internal: bool = False,
    is_x402_paid: bool = False,
) -> dict[str, Any]:
    """
    Check if a scan request is within limits.

    Args:
        identity: IP address or user ID
        identity_type: "ip" or "user"
        tier: "free", "pro", "elite"
        is_internal: True for internal/cron calls (bypass all limits)
        is_x402_paid: True for x402 paid calls (bypass all limits)

    Returns:
        {"allowed": bool, "remaining": int, "limit": int, "upsell": dict|None}
    """
    # Internal calls and x402 paid calls always pass
    if is_internal or is_x402_paid:
        return {"allowed": True, "remaining": -1, "limit": -1, "upsell": None}

    # Determine daily limit
    if tier == "elite":
        daily_limit = ELITE_DAILY_LIMIT  # 0 = unlimited
    elif tier == "pro":
        daily_limit = PRO_DAILY_LIMIT  # 100
    else:
        daily_limit = FREE_DAILY_LIMIT  # 5

    # Unlimited (elite)
    if daily_limit == 0:
        return {"allowed": True, "remaining": -1, "limit": 0, "upsell": None}

    # Check Redis for today's usage
    try:
        r = _get_redis()
        key = _identity_key(identity_type, identity)
        current = int(r.get(key) or "0")

        if current >= daily_limit:
            upsell = PREMIUM_UPSELL["free_limit_reached"]
            # Add usage stats to upsell
            upsell_with_stats = {
                **upsell,
                "scans_used": current,
                "daily_limit": daily_limit,
                "resets_at": (datetime.now(UTC) + timedelta(days=1)).strftime("%Y-%m-%dT00:00:00Z"),
            }
            return {
                "allowed": False,
                "remaining": 0,
                "limit": daily_limit,
                "upsell": upsell_with_stats,
            }

        return {
            "allowed": True,
            "remaining": daily_limit - current,
            "limit": daily_limit,
            "upsell": None,
        }

    except Exception as e:
        logger.error(f"Rate limit check failed: {e}")
        # On Redis failure, allow the scan (fail open, not closed)
        return {
            "allowed": True,
            "remaining": FREE_DAILY_LIMIT,
            "limit": FREE_DAILY_LIMIT,
            "upsell": None,
        }


def increment_scan_usage(
    identity: str,
    identity_type: str = "ip",
    tier: str = "free",
    is_internal: bool = False,
    is_x402_paid: bool = False,
) -> dict[str, Any]:
    """
    Record a scan usage. Call AFTER a successful scan.

    Returns:
        {"recorded": bool, "total_today": int, "remaining": int}
    """
    # Don't track internal or x402 paid calls
    if is_internal or is_x402_paid:
        return {"recorded": False, "total_today": 0, "remaining": -1}

    daily_limit = ELITE_DAILY_LIMIT if tier == "elite" else (PRO_DAILY_LIMIT if tier == "pro" else FREE_DAILY_LIMIT)

    try:
        r = _get_redis()
        key = _identity_key(identity_type, identity)
        pipe = r.pipeline()
        pipe.incr(key)
        pipe.expire(key, 86400 * 2)  # Expire after 2 days
        results = pipe.execute()
        total = results[0]
        remaining = (daily_limit - total) if daily_limit > 0 else -1

        return {"recorded": True, "total_today": total, "remaining": max(0, remaining)}

    except Exception as e:
        logger.error(f"Usage recording failed: {e}")
        return {"recorded": False, "total_today": 0, "remaining": -1}


def get_usage_stats(identity: str, identity_type: str = "ip") -> dict[str, Any]:
    """Get today's usage stats for an identity."""
    try:
        r = _get_redis()
        key = _identity_key(identity_type, identity)
        current = int(r.get(key) or "0")
        return {
            "identity": identity,
            "identity_type": identity_type,
            "scans_today": current,
            "free_limit": FREE_DAILY_LIMIT,
            "pro_limit": PRO_DAILY_LIMIT,
            "elite_limit": "unlimited",
            "remaining_free": max(0, FREE_DAILY_LIMIT - current),
            "resets_at": (datetime.now(UTC) + timedelta(days=1)).strftime("%Y-%m-%dT00:00:00Z"),
        }
    except Exception as e:
        logger.error(f"Usage stats failed: {e}")
        return {"identity": identity, "scans_today": 0, "error": str(e)}


def is_internal_request(request) -> bool:
    """Check if request is from internal/cron (API key header or internal IP)."""
    # Check internal API key header
    api_key = request.headers.get("X-RMI-Key", "") if hasattr(request, "headers") else ""
    if api_key == INTERNAL_API_KEY:
        return True

    # Check for x402 payment (already paid)
    x402_version = request.headers.get("X-Payment-Version", "") if hasattr(request, "headers") else ""
    if x402_version:
        return False  # x402 is not internal, but it's paid - handled separately

    # Check for authenticated user (Authorization header)
    auth = request.headers.get("Authorization", "") if hasattr(request, "headers") else ""
    if auth.startswith("Bearer "):
        return False  # Authenticated user - check their tier

    return False


def get_identity(request) -> tuple[str, str]:
    """
    Extract identity from request. Returns (identity, identity_type).

    Authenticated users: user ID from JWT
    Anonymous: IP address
    """
    # Try JWT auth first
    auth = request.headers.get("Authorization", "") if hasattr(request, "headers") else ""
    if auth.startswith("Bearer "):
        try:
            from app.auth import _verify_jwt

            payload = _verify_jwt(auth[7:])
            if payload:
                return payload.get("id", auth[7:][:32]), "user"
        except Exception:
            pass

    # Fall back to IP
    client_ip = request.client.host if hasattr(request, "client") and request.client else "unknown"
    forwarded = request.headers.get("X-Forwarded-For", "") if hasattr(request, "headers") else ""
    if forwarded:
        client_ip = forwarded.split(",")[0].strip()

    return client_ip, "ip"


def is_x402_paid_request(request) -> bool:
    """Check if request has a valid x402 payment (handled by enforcement middleware)."""
    # x402 middleware sets this header after successful payment verification
    x402_paid = request.headers.get("X-Payment-Verified", "") if hasattr(request, "headers") else ""
    return x402_paid.lower() == "true"
