const fs = require('fs');
const path = require('path');

const workspaceRoot = path.resolve(__dirname, '..');
const targetFields = ['releaseNotes', 'tagline', 'description'];
const skipDirs = new Set(['.git', 'node_modules', '.venv']);
const translationCache = new Map();

const protectedPattern = /https?:\/\/\S+|www\.\S+|\b(?:OpenAI|TypeScript|Typescript|JavaScript|Docker|GitHub|OAuth2|OAuth|MCP|API|SDK|UI|CLI|AI|AWS|JSON|CSV|FFMPEG|VCF|PDF|BTC|S3|PostgreSQL|Redis|npmjs\.com|DFWebOS|dfwebOS|Copilot|Docker Compose|MySQL|SQLite|PostgreSQL|WebDAV|WebRTC|Nostr|Bitcoin|Lightning|Tor|TLS|SSH|REST|GraphQL|WebSocket|Prometheus|Grafana|InfluxDB|MongoDB|MariaDB|Node\.js|Nextcloud|OpenStreetMap|YouTube|Plex|Sonarr|Radarr|Lidarr|Readarr|Prowlarr|SABnzbd|qBittorrent|Transmission|Home Assistant|WordPress)\b/g;

function listManifestFiles(dirPath) {
  const entries = fs.readdirSync(dirPath, { withFileTypes: true });
  const files = [];
  for (const entry of entries) {
    if (entry.isDirectory()) {
      if (skipDirs.has(entry.name)) {
        continue;
      }
      files.push(...listManifestFiles(path.join(dirPath, entry.name)));
      continue;
    }
    if (entry.name === 'dfwebos-app.yml') {
      files.push(path.join(dirPath, entry.name));
    }
  }
  return files;
}

function hasCyrillic(text) {
  return /[А-Яа-яЁё]/.test(text);
}

function parseTopLevelField(source, key) {
  const lines = source.split('\n');
  const startIndex = lines.findIndex((line) => line.startsWith(`${key}:`));
  if (startIndex === -1) {
    return null;
  }

  let endIndex = lines.length;
  for (let index = startIndex + 1; index < lines.length; index += 1) {
    if (/^[^\s#][^:]*:/.test(lines[index])) {
      endIndex = index;
      break;
    }
  }

  const firstLine = lines[startIndex];
  const inlineValue = firstLine.slice(`${key}:`.length).trim();
  let style = 'plain';
  let value = '';

  if (inlineValue === '>' || inlineValue === '>-' || inlineValue === '|' || inlineValue === '|-') {
    style = inlineValue;
    value = lines
      .slice(startIndex + 1, endIndex)
      .map((line) => (line.startsWith('  ') ? line.slice(2) : line))
      .join('\n');
  } else if (inlineValue.startsWith('"') && inlineValue.endsWith('"')) {
    style = 'double-quoted';
    value = JSON.parse(inlineValue);
  } else if (inlineValue.startsWith("'") && inlineValue.endsWith("'")) {
    style = 'single-quoted';
    value = inlineValue.slice(1, -1).replace(/''/g, "'");
  } else {
    style = 'plain';
    value = inlineValue;
  }

  return { startIndex, endIndex, style, value };
}

function protectTerms(text) {
  const tokens = [];
  const protectedText = text.replace(protectedPattern, (match) => {
    const placeholder = `ZXQQ${tokens.length}QQXZ`;
    tokens.push(match);
    return placeholder;
  });
  return { protectedText, tokens };
}

function restoreTerms(text, tokens) {
  let restored = text;
  tokens.forEach((token, index) => {
    restored = restored.replace(new RegExp(`ZXQQ${index}QQXZ`, 'g'), token);
  });
  return restored;
}

function postProcessTranslation(text) {
  return text
    .replace(/\bUI\b/g, 'интерфейс')
    .replace(/\bAI\b/g, 'ИИ')
    .replace(/AI-/g, 'ИИ-')
    .replace(/\bself-hosted\b/gi, 'самостоятельно размещаемый')
    .replace(/\bopen source\b/gi, 'с открытым исходным кодом')
    .replace(/\bbug fixes\b/gi, 'исправления ошибок')
    .replace(/\brelease notes\b/gi, 'примечания к выпуску')
    .replace(/\bkey features\b/gi, 'ключевые возможности')
    .replace(/\benterprise-ready\b/gi, 'готовность к корпоративному использованию')
    .replace(/\bAI provider\b/gi, 'провайдер ИИ')
    .replace(/\bhot reloading\b/gi, 'горячая перезагрузка');
}

async function translateText(text) {
  if (!text.trim()) {
    return text;
  }
  if (hasCyrillic(text)) {
    return text;
  }
  if (translationCache.has(text)) {
    return translationCache.get(text);
  }

  const { protectedText, tokens } = protectTerms(text);
  const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=ru&dt=t&q=${encodeURIComponent(protectedText)}`;
  const response = await fetch(url, {
    headers: {
      'User-Agent': 'Mozilla/5.0',
    },
  });

  if (!response.ok) {
    throw new Error(`Translation request failed with ${response.status}`);
  }

  const payload = await response.json();
  const translated = payload[0].map((item) => item[0]).join('');
  const restored = restoreTerms(translated, tokens);
  const processed = postProcessTranslation(restored);
  translationCache.set(text, processed);
  return processed;
}

function splitIntoChunks(value) {
  const parts = value.split(/(\n{2,})/);
  const chunks = [];

  for (const part of parts) {
    if (!part) {
      continue;
    }
    if (/^\n+$/.test(part)) {
      chunks.push({ type: 'separator', text: part });
      continue;
    }

    if (part.length <= 1500) {
      chunks.push({ type: 'text', text: part });
      continue;
    }

    const lines = part.split('\n');
    let current = [];
    let currentLength = 0;

    for (const line of lines) {
      const nextLength = currentLength + line.length + (current.length ? 1 : 0);
      if (current.length && nextLength > 1500) {
        chunks.push({ type: 'text', text: current.join('\n') });
        current = [line];
        currentLength = line.length;
      } else {
        current.push(line);
        currentLength = nextLength;
      }
    }

    if (current.length) {
      chunks.push({ type: 'text', text: current.join('\n') });
    }
  }

  return chunks;
}

async function translateMultiline(value) {
  const chunks = splitIntoChunks(value);
  const translatedChunks = [];

  for (const chunk of chunks) {
    if (chunk.type === 'separator' || !chunk.text.trim()) {
      translatedChunks.push(chunk.text);
      continue;
    }
    translatedChunks.push(await translateText(chunk.text));
  }

  return translatedChunks.join('');
}

function quoteSingle(text) {
  return `'${text.replace(/'/g, "''")}'`;
}

function formatField(key, style, value) {
  if (style === '>' || style === '>-' || style === '|' || style === '|-') {
    return [`${key}: ${style}`, ...value.split('\n').map((line) => `  ${line}`)].join('\n');
  }
  if (style === 'double-quoted') {
    return `${key}: ${JSON.stringify(value)}`;
  }
  if (style === 'single-quoted') {
    return `${key}: ${quoteSingle(value)}`;
  }
  if (/^[\p{L}\p{N} .,!?:;()\-\/]+$/u.test(value)) {
    return `${key}: ${value}`;
  }
  return `${key}: ${JSON.stringify(value)}`;
}

async function processManifest(filePath) {
  const source = fs.readFileSync(filePath, 'utf8').replace(/\r\n/g, '\n');
  const lines = source.split('\n');
  const replacements = [];

  for (const field of targetFields) {
    const parsed = parseTopLevelField(source, field);
    if (!parsed || hasCyrillic(parsed.value)) {
      continue;
    }
    const translated = await translateMultiline(parsed.value);
    replacements.push({ ...parsed, field, translated });
  }

  if (!replacements.length) {
    return false;
  }

  replacements.sort((left, right) => right.startIndex - left.startIndex);
  for (const replacement of replacements) {
    const rendered = formatField(replacement.field, replacement.style, replacement.translated).split('\n');
    lines.splice(replacement.startIndex, replacement.endIndex - replacement.startIndex, ...rendered);
  }

  fs.writeFileSync(filePath, lines.join('\n'));
  return true;
}

async function main() {
  const files = listManifestFiles(workspaceRoot);
  let changedCount = 0;

  for (const filePath of files) {
    const changed = await processManifest(filePath);
    if (changed) {
      changedCount += 1;
      console.log(`translated ${path.relative(workspaceRoot, filePath)}`);
    }
  }

  console.log(`updated manifests: ${changedCount}`);
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});