/**
 * API Key Authentication Middleware
 *
 * Provides authentication for REST API endpoints using custom API keys.
 * Validates API keys, checks permissions, and tracks usage.
 *
 * Related: Issue #48 - GDPR-compliant webshop sync refactoring
 */

import { createClient, SupabaseClient } from "https://esm.sh/@supabase/supabase-js@2.39.7";
import * as bcrypt from "https://deno.land/x/bcrypt@v0.4.1/mod.ts";

// Rate limiting configuration
const RATE_LIMIT_WINDOW_MS = 60000; // 1 minute
const RATE_LIMIT_MAX_REQUESTS = 100; // 100 requests per minute

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

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

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

/**
 * Extract API key from Authorization header
 */
export function extractApiKey(request: Request): string | null {
  const authHeader = request.headers.get("Authorization");

  if (!authHeader) {
    return null;
  }

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

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

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

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

  const currentRecord = rateLimitStore.get(apiKey);

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

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

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

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

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

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

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

    // Check if key is active
    if (!keyData.is_active) {
      return {
        valid: false,
        errorMessage: "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: "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("user_api_keys")
      .update({ last_used_at: new Date().toISOString() })
      .eq("api_key", apiKey)
      .then(() => {})
      .catch((err) => console.error("Failed to update last_used_at:", err));

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

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

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

  return result;
}

/**
 * Hash API key using bcrypt
 */
export async function hashApiKey(apiKey: string): Promise<string> {
  const salt = await bcrypt.genSalt(12);
  return await bcrypt.hash(apiKey, salt);
}

/**
 * Verify API key against hash
 */
export async function verifyApiKey(apiKey: string, hash: string): Promise<boolean> {
  try {
    return await bcrypt.compare(apiKey, hash);
  } catch (error) {
    console.error("Error verifying API key:", error);
    return false;
  }
}

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

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

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

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

  return validationResult;
}

/**
 * Create error response for API key validation failures
 */
export function createApiKeyErrorResponse(result: ApiKeyValidationResult): 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="API", error="invalid_token"',
      },
    }
  );
}

/**
 * Add rate limit headers to response
 */
export function addRateLimitHeaders(response: Response, apiKey: string): Response {
  const rateLimitResult = checkRateLimit(apiKey);

  const headers = new Headers(response.headers);
  headers.set("X-RateLimit-Limit", 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 cleanupRateLimitStore(): void {
  const now = Date.now();
  for (const [key, value] of rateLimitStore.entries()) {
    if (now > value.resetAt) {
      rateLimitStore.delete(key);
    }
  }
}

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