import { GoogleGenAI, Type } from "@google/genai";
import type { AIProvider } from '../types/providers';
import type { ReportData, Standard } from '../types';
import { validateCertificateAgainstStandard } from './validationEngine';

interface AnalyzeOptions {
  provider: AIProvider;
  apiKey: string;
  model: string;
  file: File;
  endpoint?: string;
  standardsContext?: any[];
}

const fileToGenerativePart = async (file: File) => {
  const base64EncodedDataPromise = new Promise<string>((resolve) => {
    const reader = new FileReader();
    reader.onloadend = () => resolve((reader.result as string).split(',')[1]);
    reader.readAsDataURL(file);
  });
  return {
    inlineData: { data: await base64EncodedDataPromise, mimeType: file.type },
  };
};

const cleanAndParseJson = (jsonString: string): any => {
  let cleanedString = jsonString.trim();
  
  const jsonMatch = cleanedString.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
  if (jsonMatch) {
    cleanedString = jsonMatch[1].trim();
  } else {
    const startIndex = cleanedString.indexOf('{');
    const endIndex = cleanedString.lastIndexOf('}');
    if (startIndex !== -1 && endIndex !== -1) {
      cleanedString = cleanedString.substring(startIndex, endIndex + 1);
    }
  }

  try {
    const parsed = JSON.parse(cleanedString);
    let finalData = parsed;
    if (parsed.reports) {
      finalData = parsed.reports;
    } else if (parsed.report && !parsed.identification) {
      finalData = parsed.report;
    }
    return Array.isArray(finalData) ? finalData : [finalData];
  } catch (error) {
    console.error("Failed to parse cleaned JSON:", jsonString);
    throw new Error("A resposta da IA não estava no formato JSON esperado.");
  }
};

const reportSchema = {
  type: Type.OBJECT,
  properties: {
    confidence: { type: Type.NUMBER, description: "Grau de Confiança da Análise em percentual (ex: 98)." },
    analysisSource: { type: Type.STRING, description: "Fonte da análise: 'Banco Local' ou 'Rede Neural'." },
    identification: {
      type: Type.OBJECT,
      properties: {
        product: { type: Type.STRING },
        standards: { type: Type.STRING },
        manufacturer: { type: Type.STRING },
        certificateNumber: { type: Type.STRING },
        certificateDate: { type: Type.STRING },
        batches: { type: Type.STRING },
        heats: { type: Type.STRING },
        quantity: { type: Type.STRING }
      },
      required: ["product", "standards", "manufacturer", "certificateNumber", "certificateDate", "batches", "heats", "quantity"]
    },
    compliance: {
      type: Type.OBJECT,
      properties: {
        status: { type: Type.STRING, description: "'CONFORME' ou 'NÃO CONFORME' (deixe vazio se não souber)" },
        nonComplianceReason: { type: Type.STRING },
        mechanical: {
          type: Type.ARRAY,
          items: {
            type: Type.OBJECT,
            properties: {
              property: { type: Type.STRING },
              certificate: { type: Type.STRING }
            },
            required: ["property", "certificate"]
          }
        },
        chemical: {
          type: Type.ARRAY,
          items: {
            type: Type.OBJECT,
            properties: {
              element: { type: Type.STRING },
              certificate: { type: Type.STRING }
            },
            required: ["element", "certificate"]
          }
        },
        otherTests: {
          type: Type.ARRAY,
          items: {
            type: Type.OBJECT,
            properties: {
              test: { type: Type.STRING },
              result: { type: Type.STRING }
            },
            required: ["test", "result"]
          }
        }
      },
      required: ["mechanical", "chemical", "otherTests"]
    },
    overPerformance: {
      type: Type.ARRAY,
      items: {
        type: Type.OBJECT,
        properties: {
          property: { type: Type.STRING },
          performance: { type: Type.STRING }
        },
        required: ["property", "performance"]
      },
      description: "Propriedades que superam os mínimos da norma em mais de 10%."
    },
    equivalents: {
      type: Type.ARRAY,
      items: {
        type: Type.OBJECT,
        properties: {
          system: { type: Type.STRING, description: "ex: ISO, SAE, DIN" },
          norm: { type: Type.STRING }
        },
        required: ["system", "norm"]
      },
      description: "Lista de normas internacionais equivalentes."
    }
  },
  required: ["confidence", "analysisSource", "identification", "compliance", "overPerformance", "equivalents"]
};

const arraySchema = {
  type: Type.ARRAY,
  items: reportSchema
};

export const buildAnalysisPrompt = () => {
  let prompt = `Você é o "SteelCheck", um especialista sênior em extração de dados de Certificados de Qualidade.

Sua tarefa principal é transcrever com precisão matemática e zero alucinação os dados de um Certificado de Qualidade (Mill Test Report) e gerar um JSON estruturado.

### DIRETRIZES RÍGIDAS DE TRANSCRIÇÃO (OCR DE ALTA PRECISÃO):
1. **Zero Alucinação de Normas:** No campo "standards", extraia APENAS e EXATAMENTE o que estiver escrito na imagem.
2. **Extração de Identificação:** Transcreva o produto (incluindo dimensões/bitola, ex: "CHAPA 50MM" ou "PERFIL W360x44.6"), números de certificado, lotes, corridas e quantidades exatamente como aparecem.
3. **Mapeamento Preciso de Colunas:** Mapeie composição química e propriedades mecânicas com precisão absoluta para a chave "certificate". Não preencha "norm" nem "status".
4. **Múltiplas Linhas:** Se houver múltiplas linhas de teste, gere um objeto para cada uma.
5. **Orientação:** Rotacione mentalmente a imagem para ler corretamente.

- No campo "analysisSource" do JSON, preencha EXATAMENTE com "Rede Neural".


Retorne APENAS o JSON no formato do array especificado, sem explicações ou markdown.

FORMATO DO RETORNO:
[
  {
    "confidence": 98,
    "analysisSource": "Banco Local",
    "identification": {
      "product": "PERFIL I",
      "standards": "ASTM A572 GR50 / NBR7007 AR350",
      "manufacturer": "GERDAU AÇOMINAS S.A.",
      "certificateNumber": "8183321347/000010",
      "certificateDate": "11.09.2025",
      "batches": "",
      "heats": "2715275435",
      "quantity": "8,560 T"
    },
    "compliance": {
      "status": "",
      "nonComplianceReason": "",
      "mechanical": [
        { "property": "Yield Strength (Limite de Escoamento)", "certificate": "415 MPa" }
      ],
      "chemical": [
        { "element": "C (Carbon)", "certificate": "0.22%" }
      ],
      "otherTests": []
    },
    "overPerformance": [],
    "equivalents": []
  }
]`;

  return prompt;
};

export const analyzeWithGemini = async (file: File, apiKey: string, model: string = 'gemini-2.5-flash', standardsContext?: any[]): Promise<ReportData[]> => {
  if (!apiKey) {
    throw new Error("A chave de API do Gemini não foi fornecida.");
  }

  const ai = new GoogleGenAI({ apiKey: apiKey });
  const imagePart = await fileToGenerativePart(file);
  const prompt = buildAnalysisPrompt();

  const response = await ai.models.generateContent({
    model,
    contents: { parts: [imagePart, { text: prompt }] },
    config: {
      responseMimeType: "application/json",
      responseSchema: arraySchema,
      temperature: 0.1,
    }
  });

  try {
    const text = response.text;
    const data = cleanAndParseJson(text) as ReportData[];
    
    // Apply programmatic validation
    if (standardsContext && standardsContext.length > 0) {
       let matchedStandard = standardsContext[0] as Standard;
       if (data[0] && data[0].identification && data[0].identification.standards) {
         const certNorm = data[0].identification.standards.toLowerCase();
         const found = standardsContext.find(s => 
           (s.codigo && certNorm.includes(s.codigo.toLowerCase())) || 
           (s.id_norma && certNorm.includes(s.id_norma.toLowerCase().replace(/_/g, ' ')))
         );
         if (found) matchedStandard = found;
       }
       return data.map(report => {
         report.analysisSource = 'Banco Local';
         return validateCertificateAgainstStandard(report, matchedStandard);
       });
    }
    return data;
  } catch (e) {
    if (e instanceof Error) {
      throw e;
    }
    throw new Error("A resposta da IA não estava no formato JSON esperado.");
  }
};

export const analyzeWithOpenAI = async (file: File, apiKey: string, model: string = 'gpt-4o', standardsContext?: any[]): Promise<ReportData[]> => {
  if (!apiKey) {
    throw new Error("A chave de API da OpenAI não foi fornecida.");
  }

  const base64Data = await new Promise<string>((resolve) => {
    const reader = new FileReader();
    reader.onloadend = () => resolve((reader.result as string).split(',')[1]);
    reader.readAsDataURL(file);
  });

  const prompt = buildAnalysisPrompt();

  const response = await fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model,
      messages: [
        {
          role: 'user',
          content: [
            { type: 'text', text: prompt },
            {
              type: 'image_url',
              image_url: { url: `data:${file.type};base64,${base64Data}` }
            }
          ]
        }
      ],
      max_tokens: 4096,
      temperature: 0.1,
      response_format: { type: 'json_object' }
    })
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`Erro da OpenAI: ${error.error?.message || 'Erro desconhecido'}`);
  }

  const data = await response.json();
  const content = data.choices[0]?.message?.content;
  
  if (!content) {
    throw new Error("Resposta vazia da OpenAI");
  }

  const dataJson = cleanAndParseJson(content) as ReportData[];
  if (standardsContext && standardsContext.length > 0) {
     let matchedStandard = standardsContext[0] as Standard;
     if (dataJson[0] && dataJson[0].identification && dataJson[0].identification.standards) {
       const certNorm = dataJson[0].identification.standards.toLowerCase();
       const found = standardsContext.find(s => 
         (s.codigo && certNorm.includes(s.codigo.toLowerCase())) || 
         (s.id_norma && certNorm.includes(s.id_norma.toLowerCase().replace(/_/g, ' ')))
       );
       if (found) matchedStandard = found;
     }
     return dataJson.map(report => {
       report.analysisSource = 'Banco Local';
       return validateCertificateAgainstStandard(report, matchedStandard);
     });
  }
  return dataJson;
};

export const analyzeCertificate = async (options: AnalyzeOptions): Promise<ReportData[]> => {
  const { provider, apiKey, model, file, endpoint, standardsContext } = options;

  switch (provider) {
    case 'gemini':
      return analyzeWithGemini(file, apiKey, model, standardsContext);
    case 'openai':
      return analyzeWithOpenAI(file, apiKey, model, standardsContext);
    case 'ollama':
      return analyzeWithOllama(file, endpoint!, model, standardsContext);
    case 'openrouter':
      return analyzeWithOpenRouter(file, apiKey, model, standardsContext);
    default:
      // @ts-ignore
      throw new Error(`Provedor não suportado: ${provider}`);
  }
};

export const analyzeWithOllama = async (file: File, endpoint: string, model: string = 'llama3.2-vision', standardsContext?: any[]): Promise<ReportData[]> => {
  if (!endpoint) {
    throw new Error("O endpoint do Ollama é necessário. Configure o endereço da sua VPS.");
  }

  const base64Data = await new Promise<string>((resolve) => {
    const reader = new FileReader();
    reader.onloadend = () => resolve((reader.result as string).split(',')[1]);
    reader.readAsDataURL(file);
  });

  const prompt = buildAnalysisPrompt();

  let url = `${endpoint}/api/chat`;
  
  // Check if using OpenWebUI (has /api in path but not direct Ollama)
  const useOpenWebUI = endpoint.includes('/api') || endpoint.includes('llm.reifonas.cloud');
  
  if (useOpenWebUI) {
    // Use OpenWebUI API format
    url = `${endpoint}/api/v1/chat/completions`;
  }

  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      ...(useOpenWebUI ? { 'Authorization': 'Bearer no-key-required' } : {})
    },
    body: JSON.stringify({
      model,
      messages: [
        {
          role: 'user',
          content: [
            {
              type: 'image_url',
              image_url: { url: `data:${file.type};base64,${base64Data}` }
            },
            {
              type: 'text',
              text: prompt
            }
          ]
        }
      ],
      ...(useOpenWebUI ? {} : { format: 'json' }),
      options: {
        temperature: 0.1,
        num_predict: 4096
      }
    })
  });

  if (!response.ok) {
    const error = await response.text();
    throw new Error(`Erro do Ollama/OpenWebUI: ${error}`);
  }

  const data = await response.json();
  
  // OpenWebUI format: data.choices[0].message.content
  // Ollama native format: data.message.content
  const content = data.choices?.[0]?.message?.content || data.message?.content;
  
  if (!content) {
    throw new Error("Resposta vazia do Ollama/OpenWebUI");
  }

  const dataJson = cleanAndParseJson(content) as ReportData[];
  if (standardsContext && standardsContext.length > 0) {
     let matchedStandard = standardsContext[0] as Standard;
     if (dataJson[0] && dataJson[0].identification && dataJson[0].identification.standards) {
       const certNorm = dataJson[0].identification.standards.toLowerCase();
       const found = standardsContext.find(s => 
         (s.codigo && certNorm.includes(s.codigo.toLowerCase())) || 
         (s.id_norma && certNorm.includes(s.id_norma.toLowerCase().replace(/_/g, ' ')))
       );
       if (found) matchedStandard = found;
     }
     return dataJson.map(report => {
       report.analysisSource = 'Banco Local';
       return validateCertificateAgainstStandard(report, matchedStandard);
     });
  }
  return dataJson;
};

export const analyzeWithOpenRouter = async (file: File, apiKey: string, model: string = 'openai/gpt-4o', standardsContext?: any[]): Promise<ReportData[]> => {
  if (!apiKey) {
    throw new Error("A chave de API do OpenRouter não foi fornecida.");
  }

  const base64Data = await new Promise<string>((resolve) => {
    const reader = new FileReader();
    reader.onloadend = () => resolve((reader.result as string).split(',')[1]);
    reader.readAsDataURL(file);
  });

  const prompt = buildAnalysisPrompt();

  const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json',
      'HTTP-Referer': 'https://steelcheck.app', 
      'X-Title': 'SteelCheck', 
    },
    body: JSON.stringify({
      model,
      messages: [
        {
          role: 'user',
          content: [
            { type: 'text', text: prompt },
            {
              type: 'image_url',
              image_url: { url: `data:${file.type};base64,${base64Data}` }
            }
          ]
        }
      ],
      temperature: 0.1
    })
  });

  if (!response.ok) {
    let errorMessage = 'Erro desconhecido';
    try {
      const errorData = await response.json();
      errorMessage = errorData.error?.message || JSON.stringify(errorData);
    } catch (e) {
      errorMessage = await response.text();
    }
    throw new Error(`Erro do OpenRouter: ${errorMessage}`);
  }

  const data = await response.json();
  const content = data.choices[0]?.message?.content;
  
  if (!content) {
    throw new Error("Resposta vazia do OpenRouter");
  }

  const dataJson = cleanAndParseJson(content) as ReportData[];
  if (standardsContext && standardsContext.length > 0) {
     let matchedStandard = standardsContext[0] as Standard;
     if (dataJson[0] && dataJson[0].identification && dataJson[0].identification.standards) {
       const certNorm = dataJson[0].identification.standards.toLowerCase();
       const found = standardsContext.find(s => 
         (s.codigo && certNorm.includes(s.codigo.toLowerCase())) || 
         (s.id_norma && certNorm.includes(s.id_norma.toLowerCase().replace(/_/g, ' ')))
       );
       if (found) matchedStandard = found;
     }
     return dataJson.map(report => {
       report.analysisSource = 'Banco Local';
       return validateCertificateAgainstStandard(report, matchedStandard);
     });
  }
  return dataJson;
};

export const buildStandardWithAI = async (
  options: { provider: AIProvider; apiKey: string; model: string; endpoint?: string },
  standardName: string,
  categoryDescription: string
) => {
  const prompt = `Você é um engenheiro de materiais sênior. O usuário solicitou a criação de uma tabela de limites para a norma técnica: "${standardName}".
A categoria deste material é descrita como: "${categoryDescription}".

Sua tarefa é acessar seu conhecimento sobre esta norma e gerar os limites estritos, incluindo as variações por faixa de espessura (bitola), grupos de perfil e tolerâncias de análise de produto.

RESPONDA APENAS UM JSON VÁLIDO no seguinte formato EXATO:
{
  "id_norma": "id_unico_da_norma_sem_espacos",
  "codigo": "Nome oficial da norma (ex: ASTM A572/A572M)",
  "grau": "Grau se aplicável (ex: 50)",
  "produto_especifico": "Descreva o tipo de produto",
  "limites_quimicos_corrida": {
    "produtos_gerais": [
      {
        "espessura_max_mm": valor_numerico_ou_null,
        "carbono_max": valor_numerico,
        "manganes_max": valor_numerico
      }
    ]
  },
  "tolerancias_analise_produto_astma6": {
    "carbono": {
      "tabela": [
        { "limite_corrida_ate": 0.25, "tolerancia_mais": 0.03 }
      ]
    }
  },
  "parametros_controle": {
    "mecanicos": {
      "limite_escoamento_min": valor_numerico
    }
  }
}

Regras Cruciais:
1. USE APENAS NÚMEROS (floats/ints) nos valores. NUNCA use strings como "0.25".
2. Estruture as matrizes de limite químico considerando as faixas de espessura de acordo com a norma.
3. Inclua a chave tolerancias_analise_produto_astma6 se aplicável.
4. DEVOLVA APENAS O JSON, sem nenhuma formatação ou texto explicativo ao redor.`;

  try {
    let resultJson = '';

    if (options.provider === 'gemini') {
      const ai = new GoogleGenAI({ apiKey: options.apiKey });
      const response = await ai.models.generateContent({
        model: options.model || 'gemini-2.5-flash',
        contents: prompt
      });
      resultJson = response.text || '';
    } else if (options.provider === 'openai') {
      const response = await fetch('https://api.openai.com/v1/chat/completions', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${options.apiKey}`
        },
        body: JSON.stringify({
          model: options.model || 'gpt-4o',
          messages: [{ role: "user", content: prompt }]
        })
      });
      const data = await response.json();
      resultJson = data.choices[0].message.content;
    } else if (options.provider === 'openrouter') {
      const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${options.apiKey}`,
          'HTTP-Referer': 'https://steelcheck.reifonas.cloud'
        },
        body: JSON.stringify({
          model: options.model || 'google/gemini-2.5-flash',
          messages: [{ role: "user", content: prompt }]
        })
      });
      const data = await response.json();
      resultJson = data.choices[0].message.content;
    }

    let cleanedString = resultJson.trim();
    const jsonMatch = cleanedString.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
    if (jsonMatch) cleanedString = jsonMatch[1].trim();
    else {
      const startIndex = cleanedString.indexOf('{');
      const endIndex = cleanedString.lastIndexOf('}');
      if (startIndex !== -1 && endIndex !== -1) {
        cleanedString = cleanedString.substring(startIndex, endIndex + 1);
      }
    }
    
    const parsed = JSON.parse(cleanedString);
    return parsed;
  } catch (error) {
    console.error("Erro no buildStandardWithAI:", error);
    throw new Error("Falha ao gerar norma com a IA. Tente novamente.");
  }
};