/**
 * Internal API Key Authentication Middleware
 *
 * Provides authentication for REST API endpoints using internal (non-user-based) API keys.
 * These keys allow system-level access to all webshop data without being tied to a specific user.
 *
 * Related: Issue #50 - Implement non user based API keys
 */

import { createClient, SupabaseClient } from "https://esm.sh/@supabase/supabase-js@2.39.7";

// Rate limiting configuration (stricter for internal keys)
const INTERNAL_RATE_LIMIT_WINDOW_MS = 60000; // 1 minute
const INTERNAL_RATE_LIMIT_MAX_REQUESTS = 200; // 200 requests per minute

// In-memory rate limiting store
const internalRateLimitStore = new Map<string, { count: number; resetAt: number }>();

export interface InternalApiKeyValidationResult {
  valid: boolean;
  permissions?: Record<string, unknown>;
  errorMessage?: string;
  errorCode?: string;
}

export interface RateLimitResult {
  allowed: boolean;
  remaining: number;
  resetAt: number;
}

/**
 * Extract internal API key from Authorization or authentication header
 * Supports both standard "Authorization" and non-standard "authentication" headers
 */
export function extractInternalApiKey(request: Request): string | null {
  // Try standard Authorization header first
  let authHeader = request.headers.get("Authorization");

  // Fall back to lowercase "authentication" header (used by some clients like VAPI)
  if (!authHeader) {
    authHeader = request.headers.get("authentication");
  }

  if (!authHeader) {
    return null;
  }

  // Support both "Bearer int_shopcall_xxx" and "int_shopcall_xxx" formats
  const match = authHeader.match(/^(?:Bearer\s+)?(int_shopcall_[a-zA-Z0-9_-]+)$/i);
  return match ? match[1] : null;
}

/**
 * Validate internal API key format
 */
export function isValidInternalApiKeyFormat(apiKey: string): boolean {
  return /^int_shopcall_[a-zA-Z0-9_-]{32,}$/.test(apiKey);
}

/**
 * Check rate limit for an internal API key
 */
export function checkInternalRateLimit(apiKey: string): RateLimitResult {
  const now = Date.now();
  const record = internalRateLimitStore.get(apiKey);

  // Clean up expired entries
  if (record && now > record.resetAt) {
    internalRateLimitStore.delete(apiKey);
  }

  const currentRecord = internalRateLimitStore.get(apiKey);

  if (!currentRecord) {
    // First request in this window
    const resetAt = now + INTERNAL_RATE_LIMIT_WINDOW_MS;
    internalRateLimitStore.set(apiKey, { count: 1, resetAt });
    return {
      allowed: true,
      remaining: INTERNAL_RATE_LIMIT_MAX_REQUESTS - 1,
      resetAt,
    };
  }

  if (currentRecord.count >= INTERNAL_RATE_LIMIT_MAX_REQUESTS) {
    // Rate limit exceeded
    return {
      allowed: false,
      remaining: 0,
      resetAt: currentRecord.resetAt,
    };
  }

  // Increment counter
  currentRecord.count++;
  internalRateLimitStore.set(apiKey, currentRecord);

  return {
    allowed: true,
    remaining: INTERNAL_RATE_LIMIT_MAX_REQUESTS - currentRecord.count,
    resetAt: currentRecord.resetAt,
  };
}

/**
 * Validate internal API key against database
 */
export async function validateInternalApiKey(
  apiKey: string,
  supabaseAdmin: SupabaseClient,
  requiredPermission?: string
): Promise<InternalApiKeyValidationResult> {
  try {
    // Check API key format
    if (!isValidInternalApiKeyFormat(apiKey)) {
      return {
        valid: false,
        errorMessage: "Invalid internal API key format",
        errorCode: "INVALID_FORMAT",
      };
    }

    // Query database for internal API key
    const { data: keyData, error: keyError } = await supabaseAdmin
      .from("internal_api_keys")
      .select("*")
      .eq("api_key", apiKey)
      .single();

    if (keyError || !keyData) {
      return {
        valid: false,
        errorMessage: "Internal API key not found",
        errorCode: "NOT_FOUND",
      };
    }

    // Check if key is active
    if (!keyData.is_active) {
      return {
        valid: false,
        errorMessage: "Internal API key is inactive",
        errorCode: "INACTIVE",
      };
    }

    // Check expiration
    if (keyData.expires_at) {
      const expiresAt = new Date(keyData.expires_at);
      if (expiresAt < new Date()) {
        return {
          valid: false,
          errorMessage: "Internal API key has expired",
          errorCode: "EXPIRED",
        };
      }
    }

    // Check specific permission if required
    if (requiredPermission && keyData.permissions) {
      const permissions = keyData.permissions as Record<string, unknown>;
      if (!permissions[requiredPermission]) {
        return {
          valid: false,
          errorMessage: `Missing required permission: ${requiredPermission}`,
          errorCode: "INSUFFICIENT_PERMISSIONS",
        };
      }
    }

    // Update last_used_at timestamp (non-blocking)
    supabaseAdmin
      .from("internal_api_keys")
      .update({ last_used_at: new Date().toISOString() })
      .eq("api_key", apiKey)
      .then(() => {})
      .catch((err) => console.error("Failed to update last_used_at for internal key:", err));

    return {
      valid: true,
      permissions: keyData.permissions as Record<string, unknown>,
    };
  } catch (error) {
    console.error("Error validating internal API key:", error);
    return {
      valid: false,
      errorMessage: "Internal error during API key validation",
      errorCode: "INTERNAL_ERROR",
    };
  }
}

/**
 * Generate a new internal API key
 */
export function generateInternalApiKey(): string {
  const characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-";
  const length = 48; // 48 characters for strong security
  let result = "int_shopcall_";

  for (let i = 0; i < length; i++) {
    result += characters.charAt(Math.floor(Math.random() * characters.length));
  }

  return result;
}

/**
 * Middleware function to protect Edge Function endpoints with internal API keys
 *
 * Usage:
 * ```typescript
 * const result = await requireInternalApiKey(req, supabaseAdmin);
 * if (!result.valid) {
 *   return new Response(JSON.stringify({ error: result.errorMessage }), { status: 401 });
 * }
 * ```
 */
export async function requireInternalApiKey(
  request: Request,
  supabaseAdmin: SupabaseClient,
  requiredPermission?: string
): Promise<InternalApiKeyValidationResult> {
  // Extract internal API key from header
  const apiKey = extractInternalApiKey(request);

  if (!apiKey) {
    return {
      valid: false,
      errorMessage: "Missing internal API key. Use Authorization: Bearer int_shopcall_xxx",
      errorCode: "MISSING_API_KEY",
    };
  }

  // Check rate limit
  const rateLimitResult = checkInternalRateLimit(apiKey);
  if (!rateLimitResult.allowed) {
    return {
      valid: false,
      errorMessage: `Rate limit exceeded. Try again after ${new Date(rateLimitResult.resetAt).toISOString()}`,
      errorCode: "RATE_LIMIT_EXCEEDED",
    };
  }

  // Validate internal API key
  const validationResult = await validateInternalApiKey(apiKey, supabaseAdmin, requiredPermission);

  return validationResult;
}

/**
 * Create error response for internal API key validation failures
 */
export function createInternalApiKeyErrorResponse(result: InternalApiKeyValidationResult): Response {
  const statusCode = result.errorCode === "RATE_LIMIT_EXCEEDED" ? 429 : 401;

  return new Response(
    JSON.stringify({
      error: result.errorMessage,
      code: result.errorCode,
      timestamp: new Date().toISOString(),
    }),
    {
      status: statusCode,
      headers: {
        "Content-Type": "application/json",
        "WWW-Authenticate": 'Bearer realm="Internal API", error="invalid_token"',
      },
    }
  );
}

/**
 * Add rate limit headers to response for internal API
 */
export function addInternalRateLimitHeaders(response: Response, apiKey: string): Response {
  const rateLimitResult = checkInternalRateLimit(apiKey);

  const headers = new Headers(response.headers);
  headers.set("X-RateLimit-Limit", INTERNAL_RATE_LIMIT_MAX_REQUESTS.toString());
  headers.set("X-RateLimit-Remaining", rateLimitResult.remaining.toString());
  headers.set("X-RateLimit-Reset", new Date(rateLimitResult.resetAt).toISOString());

  return new Response(response.body, {
    status: response.status,
    statusText: response.statusText,
    headers,
  });
}

/**
 * Cleanup expired rate limit entries (call periodically)
 */
export function cleanupInternalRateLimitStore(): void {
  const now = Date.now();
  for (const [key, value] of internalRateLimitStore.entries()) {
    if (now > value.resetAt) {
      internalRateLimitStore.delete(key);
    }
  }
}

// Run cleanup every 5 minutes
setInterval(cleanupInternalRateLimitStore, 5 * 60 * 1000);
