"""
EasyClaw MCP Server — Authentication & Authorization
Bearer token validation, tier checks, and usage logging.
"""

import contextvars
import hashlib
import hmac
import os
import time
from dataclasses import dataclass, field


# ---------------------------------------------------------------------------
# Auth result passed through contextvars
# ---------------------------------------------------------------------------

@dataclass
class AuthResult:
    authenticated: bool
    user_email: str | None = None
    tier: str | None = None
    daily_limit: int = 0
    daily_used: int = 0
    api_key_hash: str | None = None
    error: str | None = None
    error_code: str | None = None


_auth_context: contextvars.ContextVar[AuthResult | None] = contextvars.ContextVar(
    "auth_context", default=None
)


# ---------------------------------------------------------------------------
# Tier hierarchy
# ---------------------------------------------------------------------------

TIER_LEVELS = {"starter": 1, "pro": 2, "business": 3, "subscriber": 3}


def check_tier_access(auth: AuthResult, required_tier: str) -> bool:
    """Return True if the user's tier meets or exceeds *required_tier*."""
    user_level = TIER_LEVELS.get(auth.tier or "", 0)
    required_level = TIER_LEVELS.get(required_tier, 1)
    return user_level >= required_level


# ---------------------------------------------------------------------------
# API key hashing
# ---------------------------------------------------------------------------

def hash_api_key(api_key: str) -> str:
    """SHA-256 hash an API key for storage / lookup."""
    return hashlib.sha256(api_key.encode()).hexdigest()


# ---------------------------------------------------------------------------
# Extract Bearer token from raw ASGI scope
# ---------------------------------------------------------------------------

def extract_auth_header(scope: dict) -> str | None:
    """Pull the Authorization header value from an ASGI *scope*."""
    headers = scope.get("headers", [])
    for name, value in headers:
        if name == b"authorization":
            return value.decode("latin-1")
    return None


# ---------------------------------------------------------------------------
# Authenticate a request
# ---------------------------------------------------------------------------

def authenticate_request(auth_header: str | None) -> AuthResult:
    """Validate a Bearer token against the subscriptions table.

    Returns an AuthResult populated with subscription details, or an
    unauthenticated result with an error message.
    """
    if not auth_header:
        return AuthResult(authenticated=False, error="Missing Authorization header",
                          error_code="missing_auth")

    if not auth_header.startswith("Bearer "):
        return AuthResult(authenticated=False, error="Authorization must be Bearer token",
                          error_code="invalid_auth_format")

    token = auth_header[7:].strip()
    if not token:
        return AuthResult(authenticated=False, error="Empty Bearer token",
                          error_code="empty_token")

    key_hash = hash_api_key(token)

    # Import here to avoid circular import (database imports nothing from auth)
    from database import get_subscription_by_key_hash, get_daily_usage

    sub = get_subscription_by_key_hash(key_hash)
    if not sub:
        return AuthResult(authenticated=False, error="Invalid API key",
                          error_code="invalid_key")

    if sub["status"] != "active":
        return AuthResult(authenticated=False, error=f"Subscription {sub['status']}",
                          error_code="inactive_subscription")

    # Check subscription expiry
    if sub.get("current_period_end"):
        from datetime import datetime, timezone
        try:
            end = datetime.fromisoformat(sub["current_period_end"])
            if end.tzinfo is None:
                end = end.replace(tzinfo=timezone.utc)
            if datetime.now(timezone.utc) > end:
                return AuthResult(authenticated=False, error="Subscription expired",
                                  error_code="expired")
        except (ValueError, TypeError):
            pass  # Malformed date — allow through

    daily_used = get_daily_usage(key_hash)
    daily_limit = sub.get("daily_limit", 100)

    if daily_used >= daily_limit:
        return AuthResult(
            authenticated=False,
            error=f"Daily limit reached ({daily_used}/{daily_limit})",
            error_code="rate_limited",
            daily_limit=daily_limit,
            daily_used=daily_used,
            api_key_hash=key_hash,
        )

    return AuthResult(
        authenticated=True,
        user_email=sub.get("email"),
        tier=sub.get("tier", "starter"),
        daily_limit=daily_limit,
        daily_used=daily_used,
        api_key_hash=key_hash,
    )


# ---------------------------------------------------------------------------
# Per-tool auth enforcement
# ---------------------------------------------------------------------------

def _require_auth(tool_name: str, required_tier: str = "starter") -> AuthResult:
    """Read the auth context and enforce tier access.

    Raises ValueError on failure so FastMCP surfaces it as a tool error.
    """
    auth = _auth_context.get()
    if not auth or not auth.authenticated:
        error_msg = auth.error if auth else "Missing or invalid authorization"
        raise ValueError(f"Authentication failed: {error_msg}")
    if not check_tier_access(auth, required_tier):
        raise ValueError(
            f"Tool '{tool_name}' requires {required_tier} tier "
            f"(your tier: {auth.tier})"
        )
    return auth


# ---------------------------------------------------------------------------
# Usage logging helper
# ---------------------------------------------------------------------------

def log_tool_usage(auth_result: AuthResult, tool_name: str,
                   response_time_ms: int = 0, success: bool = True):
    """Record a tool invocation in the usage_logs table."""
    if auth_result.api_key_hash:
        from database import log_usage
        log_usage(auth_result.api_key_hash, tool_name, response_time_ms, success)


# ---------------------------------------------------------------------------
# Admin secret verification
# ---------------------------------------------------------------------------

def verify_admin_secret(request_secret: str) -> bool:
    """Timing-safe comparison of admin secret."""
    stored = os.environ.get("ADMIN_SECRET", "")
    if not stored or not request_secret:
        return False
    return hmac.compare_digest(request_secret, stored)
