#!/usr/bin/env node
'use strict';

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

const ROOT = process.cwd();
const LOCALES_DIR = path.join(ROOT, 'locales');
const TEMPLATE_PATH = path.join(LOCALES_DIR, 'template.toml');
const KO_PATH = path.join(LOCALES_DIR, 'ko.toml');
const EN_PATH = path.join(LOCALES_DIR, 'en.toml');

const SKIP_DIRS = new Set([
  '.git',
  'node_modules',
  'dist',
  'build',
  '.dart_tool',
  '.idea',
  '.vscode',
  'coverage',
  '.next',
  '.cache',
  'tmp',
  'logs',
]);

const CODE_EXTENSIONS = new Set(['.ts', '.tsx', '.dart']);

function walkDir(dirPath, files) {
  const entries = fs.readdirSync(dirPath, { withFileTypes: true });
  for (const entry of entries) {
    if (entry.isDirectory()) {
      if (SKIP_DIRS.has(entry.name)) continue;
      walkDir(path.join(dirPath, entry.name), files);
      continue;
    }
    if (!entry.isFile()) continue;
    const ext = path.extname(entry.name).toLowerCase();
    if (!CODE_EXTENSIONS.has(ext)) continue;
    files.push(path.join(dirPath, entry.name));
  }
}

function parseToml(filePath) {
  if (!fs.existsSync(filePath)) return new Map();
  const content = fs.readFileSync(filePath, 'utf8');
  const lines = content.split(/\r?\n/);
  let section = [];
  const map = new Map();
  for (const rawLine of lines) {
    const line = rawLine.trim();
    if (!line || line.startsWith('#')) continue;
    if (line.startsWith('[[') && line.endsWith(']]')) {
      const name = line.slice(2, -2).trim();
      section = name ? name.split('.').map((p) => p.trim()).filter(Boolean) : [];
      continue;
    }
    if (line.startsWith('[') && line.endsWith(']')) {
      const name = line.slice(1, -1).trim();
      section = name ? name.split('.').map((p) => p.trim()).filter(Boolean) : [];
      continue;
    }
    const eqIndex = line.indexOf('=');
    if (eqIndex === -1) continue;
    const key = line.slice(0, eqIndex).trim();
    if (!key) continue;
    let valueRaw = line.slice(eqIndex + 1).trim();
    let value = '';
    if (
      (valueRaw.startsWith('"') && valueRaw.endsWith('"')) ||
      (valueRaw.startsWith("'") && valueRaw.endsWith("'"))
    ) {
      value = valueRaw.slice(1, -1);
    } else {
      value = valueRaw;
    }
    const fullKey = [...section, key].join('.');
    map.set(fullKey, value);
  }
  return map;
}

function buildTree(keys, valuesMap) {
  const root = {};
  for (const key of keys) {
    const parts = key.split('.');
    let node = root;
    for (let i = 0; i < parts.length - 1; i++) {
      const part = parts[i];
      if (!node[part]) node[part] = {};
      node = node[part];
    }
    const leaf = parts[parts.length - 1];
    const value = valuesMap ? (valuesMap.get(key) ?? '') : '';
    node[leaf] = value;
  }
  return root;
}

function renderToml(tree) {
  const lines = [];
  function walk(node, path) {
    if (path.length) {
      lines.push(`[${path.join('.')}]`);
    }
    const keys = Object.keys(node).sort();
    const leafKeys = keys.filter((k) => typeof node[k] === 'string');
    const childKeys = keys.filter((k) => typeof node[k] === 'object');
    for (const key of leafKeys) {
      const value = node[key];
      lines.push(`${key} = ${JSON.stringify(value)}`);
    }
    for (const key of childKeys) {
      lines.push('');
      walk(node[key], [...path, key]);
    }
  }
  walk(tree, []);
  return lines.join('\n').trimEnd() + '\n';
}

function extractFallbacks() {
  const files = [];
  walkDir(ROOT, files);
  const map = new Map();

  const tsRegex = /\b(?:i18n\.)?t\s*\(\s*['"]([^'"]+)['"]\s*,\s*(['"])([\s\S]*?)\2/g;
  const dartRegex = /\btr\s*\(\s*['"]([^'"]+)['"][\s\S]*?fallback\s*:\s*(?:(\"\"\"[\s\S]*?\"\"\")|('(?:\\.|[^'])*')|(\"(?:\\.|[^\"])*\"))/g;

  for (const filePath of files) {
    const content = fs.readFileSync(filePath, 'utf8');
    let match;
    while ((match = tsRegex.exec(content)) !== null) {
      const key = match[1];
      const raw = match[3] ?? '';
      if (!map.has(key)) map.set(key, raw);
    }
    while ((match = dartRegex.exec(content)) !== null) {
      const key = match[1];
      let raw = match[2] || match[3] || match[4] || '';
      if (raw.startsWith('"""') && raw.endsWith('"""')) {
        raw = raw.slice(3, -3);
      } else if (
        (raw.startsWith('"') && raw.endsWith('"')) ||
        (raw.startsWith("'") && raw.endsWith("'"))
      ) {
        raw = raw.slice(1, -1);
      }
      if (!map.has(key)) map.set(key, raw);
    }
  }
  return map;
}

function isLongText(value) {
  if (!value) return false;
  if (value.includes('\n')) return true;
  if (value.length > 220) return true;
  if (/제\\d+조/.test(value)) return true;
  return false;
}

function isMostlyAscii(value) {
  if (!value) return false;
  const ascii = value.replace(/[^\x00-\x7F]/g, '');
  return ascii.length / value.length > 0.85;
}

const phraseMap = [
  ['로딩 중...', 'Loading...'],
  ['저장 중...', 'Saving...'],
  ['가족사 임직원', 'Affiliate employees'],
  ['일반 User', 'General user'],
  ['로그인 링크 전송', 'Send sign-in link'],
  ['링크 로그인 완료', 'Link sign-in complete'],
  ['링크 로그인', 'Link sign-in'],
  ['로그인 완료', 'Sign-in complete'],
  ['로그인 화면으로 이동', 'Go to sign-in'],
  ['회원가입 하기', 'Sign up'],
  ['회원가입', 'Sign up'],
  ['가입 완료', 'Sign-up complete'],
  ['미등록 회원', 'Unregistered member'],
  ['본인인증', 'Identity verification'],
  ['로그인', 'Sign in'],
  ['로그아웃', 'Sign out'],
  ['대시보드', 'Dashboard'],
  ['프로필', 'Profile'],
  ['내 정보', 'My profile'],
  ['QR 코드', 'QR code'],
  ['QR 인증', 'QR verification'],
  ['QR 스캔', 'Scan QR'],
  ['카메라 켜기', 'Turn on camera'],
  ['카메라', 'Camera'],
  ['스캔', 'Scan'],
  ['링크', 'Link'],
  ['승인', 'Approve'],
  ['거부', 'Reject'],
  ['완료', 'Complete'],
  ['실패', 'Failed'],
  ['성공', 'Success'],
  ['인증번호', 'Verification code'],
  ['인증', 'Verification'],
  ['비밀번호', 'Password'],
  ['재설정', 'Reset'],
  ['재설정', 'Reset'],
  ['재발송', 'Resend'],
  ['발송', 'Send'],
  ['요청', 'Request'],
  ['확인', 'Confirm'],
  ['취소', 'Cancel'],
  ['저장', 'Save'],
  ['삭제', 'Delete'],
  ['생성', 'Create'],
  ['수정', 'Edit'],
  ['등록', 'Register'],
  ['조회', 'Fetch'],
  ['목록', 'List'],
  ['상세', 'Details'],
  ['설정', 'Settings'],
  ['권한', 'Permission'],
  ['범위', 'Scope'],
  ['보안', 'Security'],
  ['사용자', 'User'],
  ['테넌트', 'Tenant'],
  ['그룹', 'Group'],
  ['이름', 'Name'],
  ['설명', 'Description'],
  ['상태', 'Status'],
  ['활성', 'Active'],
  ['비활성', 'Inactive'],
  ['복사', 'Copy'],
  ['정보', 'Information'],
  ['소속', 'Affiliation'],
  ['부서', 'Department'],
  ['부서명', 'Department name'],
  ['회사코드', 'Company code'],
  ['회사', 'Company'],
  ['조직', 'Organization'],
  ['약관동의', 'Agreements'],
  ['약관', 'Terms'],
  ['개인정보', 'Privacy'],
  ['동의', 'Consent'],
  ['접속이력', 'Access history'],
  ['접속일자', 'Access date'],
  ['접속환경', 'Access environment'],
  ['인증수단', 'Authentication method'],
  ['현황', 'Overview'],
  ['세션', 'Session'],
  ['없는', 'None'],
  ['없음', 'None'],
  ['없습니다', 'Not available'],
  ['없습니다.', 'Not available.'],
  ['알 수 없음', 'Unknown'],
  ['준비중', 'Pending'],
  ['남은 시간', 'Time remaining'],
  ['유효시간', 'Valid for'],
  ['숫자', 'Digits'],
  ['영문', 'Letters'],
  ['필수', 'Required'],
  ['선택', 'Optional'],
  ['이메일', 'Email'],
  ['휴대폰', 'Mobile'],
  ['전화번호', 'Phone number'],
  ['내용', 'Details'],
  ['추가', 'Add'],
  ['편집', 'Edit'],
  ['관리', 'Manage'],
  ['바론', 'Baron'],
  ['한라', 'Halla'],
  ['한맥', 'Hanmac'],
  ['장헌', 'Jangheon'],
  ['삼안', 'Saman'],
  ['준비 중', 'Pending'],
  ['새로고침', 'Refresh'],
  ['검색', 'Search'],
  ['추가', 'Add'],
  ['삭제', 'Delete'],
  ['수정', 'Edit'],
  ['편집', 'Edit'],
  ['생성', 'Create'],
  ['등록', 'Register'],
  ['관리', 'Manage'],
  ['목록', 'List'],
  ['상세', 'Details'],
  ['설정', 'Settings'],
  ['권한', 'Permission'],
  ['범위', 'Scope'],
  ['보안', 'Security'],
  ['비밀번호', 'Password'],
  ['이메일', 'Email'],
  ['전화번호', 'Phone number'],
  ['휴대폰', 'Mobile'],
  ['사용자', 'User'],
  ['테넌트', 'Tenant'],
  ['그룹', 'Group'],
  ['이름', 'Name'],
  ['설명', 'Description'],
  ['상태', 'Status'],
  ['활성', 'Active'],
  ['비활성', 'Inactive'],
  ['승인', 'Approve'],
  ['해지', 'Revoke'],
  ['복사', 'Copy'],
  ['요청', 'Request'],
  ['확인', 'Confirm'],
  ['취소', 'Cancel'],
  ['저장', 'Save'],
];

const keyTokenMap = [
  ['qr', 'QR'],
  ['idp', 'IDP'],
  ['oidc', 'OIDC'],
  ['api', 'API'],
  ['app', 'App'],
  ['m2m', 'M2M'],
  ['ui', 'UI'],
  ['otp', 'OTP'],
];

function translateNoun(text) {
  let result = text;
  for (const [from, to] of phraseMap) {
    result = result.split(from).join(to);
  }
  result = result.replace(/\s+/g, ' ').trim();
  return result;
}

function translateKorean(text) {
  if (!text) return text;
  const trimmed = text.trim();

  const patterns = [
    [/^(.+?)에 실패했습니다\.$/, (m, p1) => `Failed to ${translateNoun(p1)}.`],
    [/^(.+?)에 실패했습니다$/, (m, p1) => `Failed to ${translateNoun(p1)}.`],
    [/^(.+?)가 필요합니다\.$/, (m, p1) => `${translateNoun(p1)} is required.`],
    [/^(.+?)가 필요합니다$/, (m, p1) => `${translateNoun(p1)} is required.`],
    [/^(.+?)을 입력해 주세요\.$/, (m, p1) => `Please enter ${translateNoun(p1)}.`],
    [/^(.+?)을 입력해 주세요$/, (m, p1) => `Please enter ${translateNoun(p1)}.`],
    [/^(.+?)를 입력해 주세요\.$/, (m, p1) => `Please enter ${translateNoun(p1)}.`],
    [/^(.+?)를 입력해 주세요$/, (m, p1) => `Please enter ${translateNoun(p1)}.`],
    [/^(.+?)를 입력해주세요\.$/, (m, p1) => `Please enter ${translateNoun(p1)}.`],
    [/^(.+?)를 입력해주세요$/, (m, p1) => `Please enter ${translateNoun(p1)}.`],
    [/^(.+?)을 확인해 주세요\.$/, (m, p1) => `Please check ${translateNoun(p1)}.`],
    [/^(.+?)을 확인해 주세요$/, (m, p1) => `Please check ${translateNoun(p1)}.`],
    [/^(.+?)를 확인해 주세요\.$/, (m, p1) => `Please check ${translateNoun(p1)}.`],
    [/^(.+?)를 확인해 주세요$/, (m, p1) => `Please check ${translateNoun(p1)}.`],
    [/^(.+?)가 없습니다\.$/, (m, p1) => `No ${translateNoun(p1)}.`],
    [/^(.+?)가 없습니다$/, (m, p1) => `No ${translateNoun(p1)}.`],
    [/^(.+?)이 없습니다\.$/, (m, p1) => `No ${translateNoun(p1)}.`],
    [/^(.+?)이 없습니다$/, (m, p1) => `No ${translateNoun(p1)}.`],
    [/^(.+?)가 복사되었습니다\.$/, (m, p1) => `${translateNoun(p1)} copied.`],
    [/^(.+?)가 저장되었습니다\.$/, (m, p1) => `${translateNoun(p1)} saved.`],
    [/^(.+?)가 생성되었습니다\.$/, (m, p1) => `${translateNoun(p1)} created.`],
    [/^(.+?)가 수정되었습니다\.$/, (m, p1) => `${translateNoun(p1)} updated.`],
    [/^(.+?)가 삭제되었습니다\.$/, (m, p1) => `${translateNoun(p1)} deleted.`],
    [/^(.+?)를 삭제할까요\?$/, (m, p1) => `Delete ${translateNoun(p1)}?`],
  ];

  for (const [regex, fn] of patterns) {
    const match = trimmed.match(regex);
    if (match) return fn(...match);
  }

  let result = trimmed;
  const sortedPhraseMap = [...phraseMap].sort((a, b) => b[0].length - a[0].length);
  for (const [from, to] of sortedPhraseMap) {
    result = result.split(from).join(to);
  }
  return result;
}

function containsHangul(text) {
  return /[가-힣]/.test(text);
}

function keyToEnglish(key) {
  if (!key) return 'Unknown';
  const segment = key.split('.').pop() || key;
  let result = segment.replace(/_/g, ' ').replace(/\s+/g, ' ').trim();
  result = result.replace(/\b([a-z])/g, (m) => m.toUpperCase());
  for (const [from, to] of keyTokenMap) {
    const regex = new RegExp(`\\b${from}\\b`, 'gi');
    result = result.replace(regex, to);
  }
  return result || 'Unknown';
}

function main() {
  const templateMap = parseToml(TEMPLATE_PATH);
  const koMap = parseToml(KO_PATH);
  const enMap = parseToml(EN_PATH);
  const fallbacks = extractFallbacks();

  const allKeys = new Set([
    ...templateMap.keys(),
    ...koMap.keys(),
    ...enMap.keys(),
  ]);

  for (const key of allKeys) {
    const fallback = fallbacks.get(key);
    const currentKo = koMap.get(key) ?? '';
    const currentEn = enMap.get(key) ?? '';

    let nextKo = currentKo;
    if (!nextKo && fallback) {
      nextKo = fallback;
    }
    if (!nextKo) {
      nextKo = key;
    }

    let nextEn = currentEn;
    if (!nextEn) {
      const source = fallback || nextKo || key;
      if (isLongText(source)) {
        nextEn = source;
      } else if (isMostlyAscii(source)) {
        nextEn = source;
      } else {
        nextEn = translateKorean(source);
      }
    }
    if (!nextEn) {
      nextEn = key;
    }
    if (!isLongText(nextEn) && containsHangul(nextEn)) {
      nextEn = keyToEnglish(key);
    }

    koMap.set(key, nextKo);
    enMap.set(key, nextEn);
  }

  const keys = Array.from(allKeys).sort();
  fs.writeFileSync(KO_PATH, renderToml(buildTree(keys, koMap)));
  fs.writeFileSync(EN_PATH, renderToml(buildTree(keys, enMap)));
  fs.writeFileSync(TEMPLATE_PATH, renderToml(buildTree(keys, null)));
}

main();
