import type { AuditConfig, AuditScenario, AuditReport } from './types';
import { buildPrompt } from './prompt-builder';
import { parseLLMResponse, buildAuditReport } from './parser';

interface OpenAIMessage {
  role: 'system' | 'user' | 'assistant';
  content: string;
}

interface OpenAIRequest {
  model: string;
  messages: OpenAIMessage[];
  temperature: number;
  response_format: { type: 'json_object' };
}

interface AnthropicRequest {
  model: string;
  messages: { role: 'user' | 'assistant'; content: string }[];
  system?: string;
  temperature: number;
  max_tokens: number;
}

export async function runLLMAudit(
  config: AuditConfig,
  systemPrompt: string,
  userPrompt: string,
  onProgress?: (msg: string) => void,
  signal?: AbortSignal,
): Promise<string> {
  config = { ...config, apiKey: config.apiKey?.trim() || '' };
  if (config.provider === 'openai') {
    return callOpenAI(config, systemPrompt, userPrompt, onProgress, signal);
  } else if (config.provider === 'anthropic') {
    return callAnthropic(config, systemPrompt, userPrompt, onProgress, signal);
  } else if (config.provider === 'minimax') {
    return callMinimax(config, systemPrompt, userPrompt, onProgress, signal);
  } else if (config.provider === 'openrouter') {
    return callOpenRouter(config, systemPrompt, userPrompt, onProgress, signal);
  } else {
    return callOllama(config, systemPrompt, userPrompt, onProgress, signal);
  }
}

async function callOpenAI(
  config: AuditConfig,
  systemPrompt: string,
  userPrompt: string,
  onProgress?: (msg: string) => void,
  signal?: AbortSignal,
): Promise<string> {
  onProgress?.('Enviando para OpenAI...');
  const url = 'https://api.openai.com/v1/chat/completions';
  const body: OpenAIRequest = {
    model: config.model,
    messages: [
      { role: 'system', content: systemPrompt },
      { role: 'user', content: userPrompt },
    ],
    temperature: 0,
    response_format: { type: 'json_object' },
  };
  const response = await fetchWithTimeout(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${config.apiKey}`,
    },
    body: JSON.stringify(body),
  }, 600000, signal);
  const data = await response.json() as { choices?: { message?: { content?: string } }[]; error?: { message?: string } };
  if (data.error) throw new Error(`OpenAI error: ${data.error.message}`);
  const content = data.choices?.[0]?.message?.content;
  if (!content) throw new Error('OpenAI returned empty response');
  return content;
}

async function callAnthropic(
  config: AuditConfig,
  systemPrompt: string,
  userPrompt: string,
  onProgress?: (msg: string) => void,
  signal?: AbortSignal,
): Promise<string> {
  onProgress?.('Enviando para Anthropic...');
  const url = 'https://api.anthropic.com/v1/messages';
  const body: AnthropicRequest = {
    model: config.model,
    messages: [
      { role: 'user', content: `${systemPrompt}\n\n${userPrompt}` },
    ],
    system: systemPrompt,
    temperature: 0,
    max_tokens: 8192,
  };
  const response = await fetchWithTimeout(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': config.apiKey,
      'anthropic-version': '2023-06-01',
      'anthropic-dangerous-direct-browser-access': 'true',
    },
    body: JSON.stringify(body),
  }, 600000, signal);
  const data = await response.json() as { content?: { text?: string }[]; error?: { type?: string; message?: string } };
  if (data.error) throw new Error(`Anthropic error: ${data.error.message}`);
  const text = data.content?.[0]?.text;
  if (!text) throw new Error('Anthropic returned empty response');
  return text;
}

async function callMinimax(
  config: AuditConfig,
  systemPrompt: string,
  userPrompt: string,
  onProgress?: (msg: string) => void,
  signal?: AbortSignal,
): Promise<string> {
  onProgress?.('Enviando para MiniMax...');
  const url = config.baseUrl || 'https://api.minimax.chat/v1/chat/completions';
  const body = {
    model: config.model,
    messages: [
      { role: 'system', content: systemPrompt },
      { role: 'user', content: userPrompt },
    ],
    temperature: 0,
    response_format: { type: 'json_object' },
  };
  const response = await fetchWithTimeout(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${config.apiKey}`,
    },
    body: JSON.stringify(body),
  }, 600000, signal);
  const data = await response.json() as { choices?: { message?: { content?: string } }[]; error?: { message?: string } };
  if (data.error) throw new Error(`MiniMax error: ${data.error.message}`);
  const content = data.choices?.[0]?.message?.content;
  if (!content) throw new Error('MiniMax returned empty response');
  return content;
}

async function callOpenRouter(
  config: AuditConfig,
  systemPrompt: string,
  userPrompt: string,
  onProgress?: (msg: string) => void,
  signal?: AbortSignal,
): Promise<string> {
  onProgress?.('Enviando para OpenRouter...');
  const url = config.baseUrl || 'https://openrouter.ai/api/v1/chat/completions';
  const body = {
    model: config.model,
    messages: [
      { role: 'system' as const, content: systemPrompt },
      { role: 'user' as const, content: userPrompt },
    ],
    temperature: 0,
  };
  const response = await fetchWithTimeout(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${config.apiKey}`,
      'HTTP-Referer': 'https://brainwind.app',
      'X-Title': 'BrainWind NBR 6123',
    },
    body: JSON.stringify(body),
  }, 600000, signal);
  if (!response.ok) {
    const errText = await response.text();
    throw new Error(`OpenRouter HTTP ${response.status}: ${errText}`);
  }
  const data = await response.json() as { choices?: { message?: { content?: string } }[]; error?: { message?: string } };
  if (data.error) throw new Error(`OpenRouter error: ${data.error.message}`);
  const content = data.choices?.[0]?.message?.content;
  if (!content) throw new Error('OpenRouter returned empty response');
  return content;
}

async function callOllama(
  config: AuditConfig,
  systemPrompt: string,
  userPrompt: string,
  onProgress?: (msg: string) => void,
  signal?: AbortSignal,
): Promise<string> {
  onProgress?.('Enviando para Ollama (local)...');
  const baseUrl = config.baseUrl || 'http://localhost:11434';
  const body = {
    model: config.model,
    messages: [
      { role: 'system', content: systemPrompt },
      { role: 'user', content: userPrompt },
    ],
    stream: false,
    format: 'json',
    options: { temperature: 0 },
  };
  const response = await fetchWithTimeout(`${baseUrl}/api/chat`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  }, 600000, signal);
  const data = await response.json() as { message?: { content?: string }; error?: string };
  if (data.error) throw new Error(`Ollama error: ${data.error}`);
  if (!data.message?.content) throw new Error('Ollama returned empty response');
  return data.message.content;
}

export async function runAudit(
  config: AuditConfig,
  scenarios: AuditScenario[],
  onProgress?: (progress: number) => void,
  signal?: AbortSignal,
): Promise<AuditReport> {
  onProgress?.(0);
  const maxRetriesPerChunk = 2;
  const chunkSize = 5; // Reduzido de 10 para 5 devido ao alto volume de tokens do Chain of Thought
  
  const allLlmResults = [];
  let completed = 0;
  let rawResponseFull = '';
  
  for (let i = 0; i < scenarios.length; i += chunkSize) {
    if (signal?.aborted) {
      throw new Error('Abortado pelo usuário');
    }
    const chunk = scenarios.slice(i, i + chunkSize);
    let chunkSuccess = false;
    let chunkError: Error | null = null;
    
    for (let retry = 0; retry <= maxRetriesPerChunk; retry++) {
      if (signal?.aborted) {
        throw new Error('Abortado pelo usuário');
      }
      try {
        const prompt = buildPrompt(chunk);
        // Avisar a UI do progresso absoluto
        onProgress?.(completed);
        
        const rawResponse = await runLLMAudit(config, prompt.system, prompt.user, () => {
          // opcional
        }, signal);
        rawResponseFull += rawResponse + '\n\n';
        
        const llmResults = parseLLMResponse(rawResponse, chunk.map(s => s.id));
        allLlmResults.push(...llmResults);
        chunkSuccess = true;
        break; // Sucesso, sai do retry loop
      } catch (e) {
        chunkError = e instanceof Error ? e : new Error(String(e));
        console.warn(`Erro no chunk ${i} (tentativa ${retry}):`, chunkError);
      }
    }
    
    if (!chunkSuccess) {
      throw chunkError ?? new Error(`Falha ao auditar cenários no índice ${i} após retentativas.`);
    }
    completed += chunk.length;
    onProgress?.(completed);
  }

  const scenarioMap = new Map(scenarios.map(s => [s.id, { module: s.module, moduleLabel: s.moduleLabel }]));
  const report = buildAuditReport(allLlmResults, scenarioMap, config.provider, config.model, rawResponseFull);
  return report;
}

async function fetchWithTimeout(
  url: string,
  init: RequestInit,
  timeoutMs: number,
  parentSignal?: AbortSignal,
): Promise<Response> {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
  
  if (parentSignal) {
    parentSignal.addEventListener('abort', () => controller.abort());
    if (parentSignal.aborted) controller.abort();
  }

  try {
    const response = await fetch(url, { ...init, signal: controller.signal });
    return response;
  } finally {
    clearTimeout(timeout);
  }
}
