"""
API Key Management System for Organization Access

Provides secure API key generation, validation, and organization-level access control.
Supports tiered pricing plans with different rate limits and features.
"""
from __future__ import annotations

import hashlib
import hmac
import os
import secrets
import time
from dataclasses import dataclass
from enum import Enum
from typing import Optional

from app.observability.logging import get_logger


class Plan(str, Enum):
    FREE = "free"
    STARTER = "starter"
    PROFESSIONAL = "professional" 
    ENTERPRISE = "enterprise"


@dataclass(frozen=True)
class PlanLimits:
    """Rate limits and quotas for each plan tier."""
    requests_per_minute: int
    requests_per_hour: int  
    requests_per_month: int
    concurrent_sessions: int
    max_session_duration_seconds: int
    priority: int  # Higher = faster queue priority
    features: list[str]


PLAN_CONFIGS: dict[Plan, PlanLimits] = {
    Plan.FREE: PlanLimits(
        requests_per_minute=10,
        requests_per_hour=100,
        requests_per_month=200,
        concurrent_sessions=1,
        max_session_duration_seconds=300,
        priority=0,
        features=["real_time_analysis"],
    ),
    Plan.STARTER: PlanLimits(
        requests_per_minute=50,
        requests_per_hour=500,
        requests_per_month=1000,
        concurrent_sessions=2,
        max_session_duration_seconds=600,
        priority=1,
        features=["real_time_analysis", "basic_lead_scoring", "email_support", "dashboard_analytics"]
    ),
    Plan.PROFESSIONAL: PlanLimits(
        requests_per_minute=200,
        requests_per_hour=2000,
        requests_per_month=10000,
        concurrent_sessions=10,
        max_session_duration_seconds=1800,
        priority=2,
        features=["advanced_ai_coaching", "predictive_lead_scoring", "crm_integrations", "priority_support", "custom_training"]
    ),
    Plan.ENTERPRISE: PlanLimits(
        requests_per_minute=1000,
        requests_per_hour=10000,
        requests_per_month=100000,
        concurrent_sessions=50,
        max_session_duration_seconds=7200,
        priority=3,
        features=["white_label", "custom_ai_training", "dedicated_success_manager", "24_7_phone_support", "on_premise_deployment"]
    ),
}


@dataclass
class ApiKeyInfo:
    """Validated API key information."""
    key_id: str
    org_id: str
    plan: Plan
    limits: PlanLimits
    created_at: int
    is_active: bool
    allowed_ips: list[str]
    webhook_url: Optional[str] = None


# In-memory store for development; use Redis/DB in production
_api_keys: dict[str, dict] = {}
_logger = get_logger("api_keys")


def _hash_key(api_key: str) -> str:
    """Hash API key for secure storage comparison."""
    salt = os.getenv("API_KEY_SALT", "sales-voice-salt-change-in-production")
    return hashlib.sha256(f"{salt}:{api_key}".encode()).hexdigest()


def generate_api_key(
    org_id: str,
    plan: Plan = Plan.FREE,
    allowed_ips: Optional[list[str]] = None,
    webhook_url: Optional[str] = None,
) -> tuple[str, str]:
    """
    Generate a new API key for an organization.
    
    Returns:
        tuple: (api_key, key_id) - api_key should be shown once to user
    """
    key_id = f"sk_{secrets.token_hex(8)}"
    api_key = f"sv_{secrets.token_urlsafe(32)}"
    key_hash = _hash_key(api_key)
    
    _api_keys[key_hash] = {
        "key_id": key_id,
        "org_id": org_id,
        "plan": plan.value,
        "created_at": int(time.time()),
        "is_active": True,
        "allowed_ips": allowed_ips or [],
        "webhook_url": webhook_url,
    }
    
    _logger.info(
        "api_key_created",
        key_id=key_id,
        org_id=org_id,
        plan=plan.value,
    )
    
    return api_key, key_id


async def validate_api_key(
    redis,
    api_key: str,
    client_ip: Optional[str] = None,
) -> Optional[ApiKeyInfo]:
    """
    Validate an API key and return its info if valid.
    
    Checks:
    1. Key exists and is active
    2. IP allowlist (if configured)
    3. Not revoked in Redis
    """
    if not api_key or not api_key.startswith("sv_"):
        return None
    
    key_hash = _hash_key(api_key)
    
    # Check in-memory store first (for dev), then Redis
    key_data = _api_keys.get(key_hash)
    
    if not key_data:
        # Try Redis for distributed storage
        try:
            stored = await redis.hgetall(f"apikey:{key_hash}")
            if stored:
                key_data = {
                    "key_id": stored.get("key_id"),
                    "org_id": stored.get("org_id"),
                    "plan": stored.get("plan", "free"),
                    "created_at": int(stored.get("created_at", 0)),
                    "is_active": stored.get("is_active", "true") == "true",
                    "allowed_ips": (stored.get("allowed_ips") or "").split(",") if stored.get("allowed_ips") else [],
                    "webhook_url": stored.get("webhook_url"),
                }
        except Exception:
            pass
    
    if not key_data:
        _logger.warning("api_key_not_found", key_prefix=api_key[:10])
        return None
    
    if not key_data.get("is_active", True):
        _logger.warning("api_key_inactive", key_id=key_data.get("key_id"))
        return None
    
    # Check IP allowlist
    allowed_ips = key_data.get("allowed_ips", [])
    if allowed_ips and client_ip and client_ip not in allowed_ips:
        _logger.warning(
            "api_key_ip_denied",
            key_id=key_data.get("key_id"),
            client_ip=client_ip,
        )
        return None
    
    # Check if revoked in Redis
    try:
        revoked = await redis.get(f"apikey:revoked:{key_data['key_id']}")
        if revoked:
            _logger.warning("api_key_revoked", key_id=key_data.get("key_id"))
            return None
    except Exception:
        pass
    
    plan = Plan(key_data.get("plan", "free"))
    
    return ApiKeyInfo(
        key_id=key_data["key_id"],
        org_id=key_data["org_id"],
        plan=plan,
        limits=PLAN_CONFIGS[plan],
        created_at=key_data.get("created_at", 0),
        is_active=True,
        allowed_ips=allowed_ips,
        webhook_url=key_data.get("webhook_url"),
    )


async def revoke_api_key(redis, key_id: str) -> bool:
    """Revoke an API key by its ID."""
    try:
        await redis.set(f"apikey:revoked:{key_id}", "1", ex=86400 * 365)  # 1 year TTL
        _logger.info("api_key_revoked", key_id=key_id)
        return True
    except Exception as e:
        _logger.error("api_key_revoke_failed", key_id=key_id, error=str(e))
        return False


def generate_request_signature(
    api_key: str,
    timestamp: int,
    body: bytes,
) -> str:
    """
    Generate HMAC signature for request validation.
    
    Used to verify request integrity and prevent replay attacks.
    """
    message = f"{timestamp}.{body.decode('utf-8', errors='replace')}"
    signature = hmac.new(
        api_key.encode(),
        message.encode(),
        hashlib.sha256,
    ).hexdigest()
    return f"v1={signature}"


def verify_request_signature(
    api_key: str,
    timestamp: int,
    body: bytes,
    signature: str,
    max_age_seconds: int = 300,
) -> bool:
    """
    Verify request signature and timestamp.
    
    Returns True if signature is valid and timestamp is fresh.
    """
    # Check timestamp freshness
    now = int(time.time())
    if abs(now - timestamp) > max_age_seconds:
        _logger.warning("signature_timestamp_expired", age=abs(now - timestamp))
        return False
    
    expected = generate_request_signature(api_key, timestamp, body)
    return hmac.compare_digest(expected, signature)


# Bootstrap: create a master API key from environment if configured
def _bootstrap_master_key():
    master_key = os.getenv("MASTER_API_KEY")
    master_org = os.getenv("MASTER_ORG_ID", "system")
    
    if master_key:
        key_hash = _hash_key(master_key)
        _api_keys[key_hash] = {
            "key_id": "sk_master",
            "org_id": master_org,
            "plan": Plan.ENTERPRISE.value,
            "created_at": int(time.time()),
            "is_active": True,
            "allowed_ips": [],
            "webhook_url": None,
        }
        _logger.info("master_api_key_configured", org_id=master_org)


_bootstrap_master_key()
