import { serve } from "https://deno.land/std@0.190.0/http/server.ts";
import { createEdgeFunction, type EdgeFunctionContext } from '../_shared/edgeFunctionWrapper.ts';
import { corsHeaders } from '../_shared/cors.ts';
import { addSpanEvent } from '../_shared/logger.ts';

interface IPLocationResponse {
  country: string;
  countryCode: string;
  measurementSystem: 'metric' | 'imperial';
}

// Simple in-memory rate limiter
const rateLimitMap = new Map<string, { count: number; resetAt: number }>();
const RATE_LIMIT_WINDOW = 60000; // 1 minute
const MAX_REQUESTS = 10; // 10 requests per minute per IP
const MAX_MAP_SIZE = 10000;

let cleanupFailureCount = 0;
const MAX_CLEANUP_FAILURES = 5;
const CLEANUP_FAILURE_RESET_INTERVAL = 300000; // 5 minutes

function cleanupExpiredEntries() {
  try {
    const now = Date.now();
    for (const [ip, data] of rateLimitMap.entries()) {
      if (now > data.resetAt) {
        rateLimitMap.delete(ip);
      }
    }
    if (cleanupFailureCount > 0) {
      cleanupFailureCount = 0;
    }
  } catch (error: unknown) {
    cleanupFailureCount++;
    
    if (cleanupFailureCount >= MAX_CLEANUP_FAILURES) {
      try {
        const entriesToClear = Math.floor(rateLimitMap.size * 0.5);
        const sortedEntries = Array.from(rateLimitMap.entries())
          .sort((a, b) => a[1].resetAt - b[1].resetAt);
        
        for (let i = 0; i < entriesToClear && i < sortedEntries.length; i++) {
          rateLimitMap.delete(sortedEntries[i][0]);
        }
        
        cleanupFailureCount = 0;
      } catch {
        rateLimitMap.clear();
        cleanupFailureCount = 0;
      }
    }
  }
}

setInterval(() => {
  if (cleanupFailureCount > 0) {
    cleanupFailureCount = 0;
  }
}, CLEANUP_FAILURE_RESET_INTERVAL);

function checkRateLimit(ip: string): { allowed: boolean; retryAfter?: number } {
  const now = Date.now();
  const existing = rateLimitMap.get(ip);

  if (existing && now <= existing.resetAt) {
    if (existing.count >= MAX_REQUESTS) {
      const retryAfter = Math.ceil((existing.resetAt - now) / 1000);
      return { allowed: false, retryAfter };
    }
    existing.count++;
    return { allowed: true };
  }

  if (!existing && rateLimitMap.size >= MAX_MAP_SIZE) {
    cleanupExpiredEntries();
    
    if (rateLimitMap.size >= MAX_MAP_SIZE) {
      try {
        const toDelete = Math.floor(MAX_MAP_SIZE * 0.3);
        const sortedEntries = Array.from(rateLimitMap.entries())
          .sort((a, b) => a[1].resetAt - b[1].resetAt);
        
        for (let i = 0; i < toDelete && i < sortedEntries.length; i++) {
          rateLimitMap.delete(sortedEntries[i][0]);
        }
      } catch {
        rateLimitMap.clear();
      }
    }
  }
  
  rateLimitMap.set(ip, { count: 1, resetAt: now + RATE_LIMIT_WINDOW });
  return { allowed: true };
}

setInterval(cleanupExpiredEntries, Math.min(RATE_LIMIT_WINDOW / 2, 30000));

const handler = async (req: Request, { span, requestId }: EdgeFunctionContext) => {
  // Get client IP
  const forwarded = req.headers.get('x-forwarded-for');
  const realIP = req.headers.get('x-real-ip');
  const clientIP = forwarded?.split(',')[0] || realIP || '8.8.8.8';
  
  // Check rate limit
  const rateLimit = checkRateLimit(clientIP);
  if (!rateLimit.allowed) {
    addSpanEvent(span, 'rate_limit_exceeded', { clientIP: clientIP.substring(0, 8) + '...' });
    return new Response(
      JSON.stringify({
        error: 'Rate limit exceeded',
        message: 'Too many requests. Please try again later.',
        retryAfter: rateLimit.retryAfter
      }),
      {
        status: 429,
        headers: {
          'Retry-After': String(rateLimit.retryAfter || 60)
        }
      }
    );
  }
  
  addSpanEvent(span, 'detecting_location', { requestId });

  // Use configurable geolocation service
  const geoApiUrl = Deno.env.get('GEOLOCATION_API_URL') || 'http://ip-api.com/json';
  const geoApiFields = Deno.env.get('GEOLOCATION_API_FIELDS') || 'status,country,countryCode';
  
  let geoResponse;
  try {
    geoResponse = await fetch(`${geoApiUrl}/${clientIP}?fields=${geoApiFields}`);
  } catch (fetchError) {
    addSpanEvent(span, 'network_error', { error: fetchError instanceof Error ? fetchError.message : String(fetchError) });
    throw new Error('Network error: Unable to reach geolocation service');
  }
  
  if (!geoResponse.ok) {
    throw new Error(`Geolocation service returned ${geoResponse.status}: ${geoResponse.statusText}`);
  }
  
  let geoData;
  try {
    geoData = await geoResponse.json();
  } catch (parseError) {
    addSpanEvent(span, 'parse_error', { error: parseError instanceof Error ? parseError.message : String(parseError) });
    throw new Error('Invalid response format from geolocation service');
  }
  
  if (geoData.status !== 'success') {
    throw new Error(`Geolocation failed: ${geoData.message || 'Invalid location data'}`);
  }

  // Countries that primarily use imperial system
  const imperialCountries = ['US', 'LR', 'MM'];
  const measurementSystem = imperialCountries.includes(geoData.countryCode) ? 'imperial' : 'metric';

  const result: IPLocationResponse = {
    country: geoData.country,
    countryCode: geoData.countryCode,
    measurementSystem
  };

  addSpanEvent(span, 'location_detected', { 
    country: result.country, 
    countryCode: result.countryCode, 
    measurementSystem: result.measurementSystem
  });

  return result;
};

serve(createEdgeFunction({
  name: 'detect-location',
  requireAuth: false,
  corsHeaders,
  enableTracing: true,
  logRequests: true,
}, handler));
