'use strict';

function extractBalancedJson(raw) {
  const text = String(raw || '');
  const start = [...text].findIndex((character) => character === '{' || character === '[');
  if (start < 0) return '';
  const open = text[start];
  const close = open === '{' ? '}' : ']';
  let depth = 0;
  let inString = false;
  let escaped = false;
  for (let index = start; index < text.length; index += 1) {
    const character = text[index];
    if (inString) {
      if (escaped) escaped = false;
      else if (character === '\\') escaped = true;
      else if (character === '"') inString = false;
      continue;
    }
    if (character === '"') {
      inString = true;
      continue;
    }
    if (character === open) depth += 1;
    else if (character === close) {
      depth -= 1;
      if (depth === 0) return text.slice(start, index + 1);
    }
  }
  return text.slice(start);
}

function repairJsonCandidate(candidate) {
  return String(candidate || '')
    .replace(/\r/g, '')
    .replace(/,\s*([}\]])/g, '$1')
    .replace(/}\s*(?={)/g, '},')
    .replace(/]\s*(?="[\w-]+"\s*:)/g, '],')
    .replace(/}\s*(?="[\w-]+"\s*:)/g, '},');
}

function findArrayContent(text, key) {
  const source = String(text || '');
  const match = new RegExp(`"${key}"\\s*:`, 'i').exec(source);
  const startSearch = match ? match.index + match[0].length : 0;
  const arrayStart = source.indexOf('[', startSearch);
  if (arrayStart < 0) return '';
  let depth = 0;
  let inString = false;
  let escaped = false;
  for (let index = arrayStart; index < source.length; index += 1) {
    const character = source[index];
    if (inString) {
      if (escaped) escaped = false;
      else if (character === '\\') escaped = true;
      else if (character === '"') inString = false;
      continue;
    }
    if (character === '"') {
      inString = true;
      continue;
    }
    if (character === '[') depth += 1;
    else if (character === ']') {
      depth -= 1;
      if (depth === 0) return source.slice(arrayStart + 1, index);
    }
  }
  return source.slice(arrayStart + 1);
}

function extractObjectFragments(arrayContent) {
  const fragments = [];
  const source = String(arrayContent || '');
  let objectStart = -1;
  let depth = 0;
  let inString = false;
  let escaped = false;
  for (let index = 0; index < source.length; index += 1) {
    const character = source[index];
    if (inString) {
      if (escaped) escaped = false;
      else if (character === '\\') escaped = true;
      else if (character === '"') inString = false;
      continue;
    }
    if (character === '"') {
      inString = true;
      continue;
    }
    if (character === '{') {
      if (depth === 0) objectStart = index;
      depth += 1;
    } else if (character === '}') {
      depth -= 1;
      if (depth === 0 && objectStart >= 0) {
        fragments.push(source.slice(objectStart, index + 1));
        objectStart = -1;
      }
    }
  }
  return fragments;
}

function parseFragment(fragment) {
  const attempts = [fragment, repairJsonCandidate(fragment)]
    .filter((value, index, values) => value && values.indexOf(value) === index);
  for (const attempt of attempts) {
    try {
      return JSON.parse(attempt);
    } catch (_) {}
  }
  return null;
}

function extractPartialRows(raw, candidate) {
  const source = `${String(candidate || '')}\n${String(raw || '')}`;
  for (const key of ['filas', 'productos', 'items', 'rows', 'insumos']) {
    const arrayContent = findArrayContent(source, key);
    if (!arrayContent) continue;
    const rows = extractObjectFragments(arrayContent)
      .map(parseFragment)
      .filter((row) => row && typeof row === 'object' && !Array.isArray(row));
    if (rows.length) return { filas: rows, parcial: true };
  }
  if (/^\s*\[/.test(String(candidate || ''))) {
    const rows = extractObjectFragments(String(candidate || ''))
      .map(parseFragment)
      .filter((row) => row && typeof row === 'object' && !Array.isArray(row));
    if (rows.length) return { filas: rows, parcial: true };
  }
  return null;
}

function extractJson(text) {
  const raw = String(text || '')
    .trim()
    .replace(/^```(?:json)?\s*/i, '')
    .replace(/\s*```$/i, '');
  const candidate = extractBalancedJson(raw);
  if (!candidate) throw new Error('Claude no devolvio JSON valido');
  const attempts = [candidate, repairJsonCandidate(candidate)]
    .filter((value, index, values) => value && values.indexOf(value) === index);
  let lastError = null;
  for (const attempt of attempts) {
    try {
      return JSON.parse(attempt);
    } catch (error) {
      lastError = error;
    }
  }
  const partial = extractPartialRows(raw, candidate);
  if (partial) return partial;
  const error = new Error('La IA devolvio JSON incompleto o mal formado. Vuelve a previsualizar para reintentar.');
  error.cause = lastError;
  throw error;
}

module.exports = {
  extractJson,
  extractBalancedJson,
  repairJsonCandidate,
  extractPartialRows,
};
