/**
 * BookDate: Test AI Provider Connection & Fetch Models
 * Documentation: documentation/features/bookdate-prd.md
 */

import { NextRequest, NextResponse } from 'next/server';
import { requireAuth, AuthenticatedRequest } from '@/lib/middleware/auth';
import { RMABLogger } from '@/lib/utils/logger';

const logger = RMABLogger.create('API.BookDate.TestConnection');

// Fetch available Claude models from the Anthropic API
async function fetchClaudeModels(apiKey: string): Promise<{ id: string; name: string }[]> {
  const allModels: { id: string; name: string }[] = [];
  let afterId: string | undefined;

  // Paginate through all available models
  do {
    const params = new URLSearchParams({ limit: '1000' });
    if (afterId) {
      params.set('after_id', afterId);
    }

    const response = await fetch(
      `https://api.anthropic.com/v1/models?${params.toString()}`,
      {
        headers: {
          'x-api-key': apiKey,
          'anthropic-version': '2023-06-01',
        },
      }
    );

    if (!response.ok) {
      const errorText = await response.text();
      logger.error('Claude API error', { error: errorText });
      throw new Error('Invalid Claude API key or connection failed');
    }

    const data = await response.json();

    for (const model of data.data) {
      allModels.push({
        id: model.id,
        name: model.display_name || model.id,
      });
    }

    afterId = data.has_more ? data.last_id : undefined;
  } while (afterId);

  return allModels;
}

// Fetch available Gemini models from the Google API
async function fetchGeminiModels(apiKey: string): Promise<{ id: string; name: string }[]> {
  const response = await fetch(
    'https://generativelanguage.googleapis.com/v1beta/models',
    { headers: { 'x-goog-api-key': apiKey } }
  );

  if (!response.ok) {
    const errorText = await response.text();
    logger.error('Gemini API error', { error: errorText });
    throw new Error('Invalid Gemini API key or connection failed');
  }

  const data = await response.json();

  return (data.models || [])
    .filter((m: any) => m.name?.startsWith('models/gemini-') && m.supportedGenerationMethods?.includes('generateContent'))
    .map((m: any) => ({
      id: m.name.replace('models/', ''),
      name: m.displayName || m.name.replace('models/', ''),
    }))
    .sort((a: any, b: any) => a.name.localeCompare(b.name));
}

// Helper functions for custom provider
function isValidBaseUrl(url: string): boolean {
  try {
    const parsed = new URL(url);
    return parsed.protocol === 'http:' || parsed.protocol === 'https:';
  } catch {
    return false;
  }
}

function normalizeBaseUrl(url: string): string {
  return url.replace(/\/$/, ''); // Remove trailing slash
}

async function authenticatedHandler(req: AuthenticatedRequest) {
  try {
    const body = await req.json();
    const { provider, apiKey, baseUrl, useSavedKey } = body;

    // Validate provider
    if (!provider) {
      return NextResponse.json(
        { error: 'Provider is required' },
        { status: 400 }
      );
    }

    if (!['openai', 'claude', 'custom', 'gemini'].includes(provider)) {
      return NextResponse.json(
        { error: 'Invalid provider. Must be "openai", "claude", "custom", or "gemini"' },
        { status: 400 }
      );
    }

    // Custom provider requires baseUrl
    if (provider === 'custom') {
      if (!baseUrl && !useSavedKey) {
        return NextResponse.json(
          { error: 'Base URL is required for custom provider' },
          { status: 400 }
        );
      }

      const urlToValidate = useSavedKey ? null : baseUrl; // Will check saved URL later if useSavedKey
      if (urlToValidate && !isValidBaseUrl(urlToValidate)) {
        return NextResponse.json(
          { error: 'Invalid base URL format. Must start with http:// or https://' },
          { status: 400 }
        );
      }
    }

    // Get API key and baseUrl from saved global config if useSavedKey is true
    let testApiKey = apiKey;
    let testBaseUrl = baseUrl;
    if (useSavedKey) {
      const { prisma } = await import('@/lib/db');
      const { getEncryptionService } = await import('@/lib/services/encryption.service');

      const config = await prisma.bookDateConfig.findFirst();

      if (!config || !config.apiKey) {
        return NextResponse.json(
          { error: 'No saved configuration found' },
          { status: 400 }
        );
      }

      const encryptionService = getEncryptionService();
      try {
        testApiKey = encryptionService.decrypt(config.apiKey);
      } catch {
        // Allow empty API key for custom provider
        if (provider !== 'custom') {
          return NextResponse.json(
            { error: 'Failed to decrypt saved API key' },
            { status: 500 }
          );
        }
        testApiKey = '';
      }

      if (provider === 'custom') {
        testBaseUrl = config.baseUrl || '';
        if (!testBaseUrl) {
          return NextResponse.json(
            { error: 'No saved base URL found for custom provider' },
            { status: 400 }
          );
        }
      }
    }

    // API key required for OpenAI and Claude
    if (!testApiKey && provider !== 'custom') {
      return NextResponse.json(
        { error: 'API key is required' },
        { status: 400 }
      );
    }

    let models = [];

    if (provider === 'openai') {
      // OpenAI: Fetch models from API
      const response = await fetch('https://api.openai.com/v1/models', {
        headers: {
          'Authorization': `Bearer ${testApiKey}`,
        },
      });

      if (!response.ok) {
        const errorText = await response.text();
        logger.error('OpenAI API error', { error: errorText });
        return NextResponse.json(
          { error: 'Invalid OpenAI API key or connection failed' },
          { status: 400 }
        );
      }

      const data = await response.json();

      // Filter to relevant GPT models
      models = data.data
        .filter((m: any) => m.id.startsWith('gpt-') && m.id.includes('4'))
        .map((m: any) => ({
          id: m.id,
          name: m.id,
        }))
        .sort((a: any, b: any) => a.name.localeCompare(b.name));

    } else if (provider === 'claude') {
      // Claude: Fetch models dynamically from the Anthropic Models API
      try {
        models = await fetchClaudeModels(testApiKey);
      } catch {
        return NextResponse.json(
          { error: 'Invalid Claude API key or connection failed' },
          { status: 400 }
        );
      }
    } else if (provider === 'gemini') {
      // Gemini: Fetch models dynamically from the Google API
      try {
        models = await fetchGeminiModels(testApiKey);
      } catch {
        return NextResponse.json(
          { error: 'Invalid Gemini API key or connection failed' },
          { status: 400 }
        );
      }
    } else if (provider === 'custom') {
      // Custom: Fetch models from custom OpenAI-compatible endpoint
      const normalizedUrl = normalizeBaseUrl(testBaseUrl);
      const modelsEndpoint = normalizedUrl + '/models';

      const headers: Record<string, string> = {};
      if (testApiKey) {
        headers['Authorization'] = `Bearer ${testApiKey}`;
      }

      try {
        const response = await fetch(modelsEndpoint, {
          method: 'GET',
          headers,
        });

        if (!response.ok) {
          const errorText = await response.text();
          logger.error('Custom provider connection error', { error: errorText });
          // Return 400 (not the external service's status) to prevent triggering logout on 401
          return NextResponse.json(
            { error: `Failed to connect to custom provider: ${response.status} ${errorText}` },
            { status: 400 }
          );
        }

        const data = await response.json();

        // Handle multiple response formats
        let modelsList = [];
        if (Array.isArray(data?.data)) {
          // OpenAI format: { data: [...] }
          modelsList = data.data.map((m: any) => ({
            id: m.id,
            name: m.name || m.id,
          }));
        } else if (Array.isArray(data)) {
          // Direct array format
          modelsList = data.map((m: any) => ({
            id: m.id || m,
            name: m.name || m.id || m,
          }));
        } else {
          // Unable to parse, but connection successful
          return NextResponse.json({
            success: true,
            models: [],
            message: 'Connected successfully but could not parse models list. You may need to enter model name manually.',
          });
        }

        models = modelsList;
      } catch (error: any) {
        logger.error('Custom provider network error', { error: error.message });
        return NextResponse.json(
          { error: `Network error connecting to custom provider: ${error.message}` },
          { status: 500 }
        );
      }
    }

    return NextResponse.json({
      success: true,
      models,
      provider,
    });

  } catch (error: any) {
    logger.error('Test connection error', { error: error instanceof Error ? error.message : String(error) });
    return NextResponse.json(
      { error: error.message || 'Connection test failed' },
      { status: 500 }
    );
  }
}

// Unauthenticated handler for setup wizard
async function unauthenticatedHandler(req: NextRequest) {
  try {
    const body = await req.json();
    const { provider, apiKey, baseUrl, useSavedKey } = body;

    // During setup, useSavedKey should not be used (no auth context)
    if (useSavedKey) {
      return NextResponse.json(
        { error: 'Authentication required to use saved API key' },
        { status: 401 }
      );
    }

    // Validate provider
    if (!provider) {
      return NextResponse.json(
        { error: 'Provider is required' },
        { status: 400 }
      );
    }

    if (!['openai', 'claude', 'custom', 'gemini'].includes(provider)) {
      return NextResponse.json(
        { error: 'Invalid provider. Must be "openai", "claude", "custom", or "gemini"' },
        { status: 400 }
      );
    }

    // Custom provider requires baseUrl
    if (provider === 'custom') {
      if (!baseUrl) {
        return NextResponse.json(
          { error: 'Base URL is required for custom provider' },
          { status: 400 }
        );
      }

      if (!isValidBaseUrl(baseUrl)) {
        return NextResponse.json(
          { error: 'Invalid base URL format. Must start with http:// or https://' },
          { status: 400 }
        );
      }
    }

    // API key required for OpenAI and Claude
    if (!apiKey && provider !== 'custom') {
      return NextResponse.json(
        { error: 'API key is required' },
        { status: 400 }
      );
    }

    let models = [];

    if (provider === 'openai') {
      // OpenAI: Fetch models from API
      const response = await fetch('https://api.openai.com/v1/models', {
        headers: {
          'Authorization': `Bearer ${apiKey}`,
        },
      });

      if (!response.ok) {
        const errorText = await response.text();
        logger.error('OpenAI API error', { error: errorText });
        return NextResponse.json(
          { error: 'Invalid OpenAI API key or connection failed' },
          { status: 400 }
        );
      }

      const data = await response.json();

      // Filter to relevant GPT models
      models = data.data
        .filter((m: any) => m.id.startsWith('gpt-') && m.id.includes('4'))
        .map((m: any) => ({
          id: m.id,
          name: m.id,
        }))
        .sort((a: any, b: any) => a.name.localeCompare(b.name));

    } else if (provider === 'claude') {
      // Claude: Fetch models dynamically from the Anthropic Models API
      try {
        models = await fetchClaudeModels(apiKey);
      } catch {
        return NextResponse.json(
          { error: 'Invalid Claude API key or connection failed' },
          { status: 400 }
        );
      }
    } else if (provider === 'gemini') {
      // Gemini: Fetch models dynamically
      try {
        models = await fetchGeminiModels(apiKey);
      } catch {
        return NextResponse.json(
          { error: 'Invalid Gemini API key or connection failed' },
          { status: 400 }
        );
      }
    } else if (provider === 'custom') {
      // Custom: Fetch models from custom OpenAI-compatible endpoint
      const normalizedUrl = normalizeBaseUrl(baseUrl);
      const modelsEndpoint = normalizedUrl + '/models';

      const headers: Record<string, string> = {};
      if (apiKey) {
        headers['Authorization'] = `Bearer ${apiKey}`;
      }

      try {
        const response = await fetch(modelsEndpoint, {
          method: 'GET',
          headers,
        });

        if (!response.ok) {
          const errorText = await response.text();
          logger.error('Custom provider connection error', { error: errorText });
          // Return 400 (not the external service's status) to prevent triggering logout on 401
          return NextResponse.json(
            { error: `Failed to connect to custom provider: ${response.status} ${errorText}` },
            { status: 400 }
          );
        }

        const data = await response.json();

        // Handle multiple response formats
        let modelsList = [];
        if (Array.isArray(data?.data)) {
          // OpenAI format: { data: [...] }
          modelsList = data.data.map((m: any) => ({
            id: m.id,
            name: m.name || m.id,
          }));
        } else if (Array.isArray(data)) {
          // Direct array format
          modelsList = data.map((m: any) => ({
            id: m.id || m,
            name: m.name || m.id || m,
          }));
        } else {
          // Unable to parse, but connection successful
          return NextResponse.json({
            success: true,
            models: [],
            message: 'Connected successfully but could not parse models list. You may need to enter model name manually.',
          });
        }

        models = modelsList;
      } catch (error: any) {
        logger.error('Custom provider network error', { error: error.message });
        return NextResponse.json(
          { error: `Network error connecting to custom provider: ${error.message}` },
          { status: 500 }
        );
      }
    }

    return NextResponse.json({
      success: true,
      models,
      provider,
    });

  } catch (error: any) {
    logger.error('Test connection error', { error: error instanceof Error ? error.message : String(error) });
    return NextResponse.json(
      { error: error.message || 'Connection test failed' },
      { status: 500 }
    );
  }
}

export async function POST(req: NextRequest) {
  // Check if request has authorization header
  const authHeader = req.headers.get('authorization');

  if (authHeader) {
    // Authenticated request (from settings page)
    return requireAuth(req, authenticatedHandler);
  } else {
    // Unauthenticated request (from setup wizard)
    return unauthenticatedHandler(req);
  }
}
