import { readFileSync } from "node:fs";
import type { Messages } from "./index.ts";

export interface ExtractedTranslationKey {
  key: string;
  file?: string;
  offset: number;
}

export function extractTranslationKeys(source: string, file?: string): ExtractedTranslationKey[] {
  const found = new Map<string, ExtractedTranslationKey>();
  const patterns = [
    /(?:\b(?:t|\$t)|\bi18n\.t)\s*\(\s*(["'])([^"']+)\1/g,
    /\bdata-i18n\s*=\s*(["'])([^"']+)\1/g,
    /\{t:([A-Za-z0-9_.:-]+)\}/g,
  ];
  for (const pattern of patterns) {
    for (const match of source.matchAll(pattern)) {
      const key = (match[2] ?? match[1])!.trim();
      if (key && !found.has(key)) found.set(key, { key, file, offset: match.index });
    }
  }
  return [...found.values()].sort((left, right) => left.key.localeCompare(right.key));
}

export function extractTranslationKeysFromFiles(
  files: Iterable<string>,
): ExtractedTranslationKey[] {
  const found = new Map<string, ExtractedTranslationKey>();
  for (const file of files) {
    for (const item of extractTranslationKeys(readFileSync(file, "utf8"), file)) {
      found.set(`${item.file}:${item.key}`, item);
    }
  }
  return [...found.values()].sort(
    (left, right) =>
      String(left.file).localeCompare(String(right.file)) || left.key.localeCompare(right.key),
  );
}

export function flattenMessageKeys(messages: Messages, prefix = ""): string[] {
  const keys: string[] = [];
  for (const [name, value] of Object.entries(messages)) {
    const key = prefix ? `${prefix}.${name}` : name;
    if (value && typeof value === "object" && !Array.isArray(value)) {
      keys.push(...flattenMessageKeys(value as Messages, key));
    } else keys.push(key);
  }
  return keys.sort();
}

export function auditLocaleKeys(
  messages: Record<string, Messages>,
  referenceLocale: string,
): Record<string, { missing: string[]; extra: string[] }> {
  const reference = new Set(flattenMessageKeys(messages[referenceLocale] ?? {}));
  const result: Record<string, { missing: string[]; extra: string[] }> = {};
  for (const [locale, value] of Object.entries(messages)) {
    const keys = new Set(flattenMessageKeys(value));
    result[locale] = {
      missing: [...reference].filter((key) => !keys.has(key)).sort(),
      extra: [...keys].filter((key) => !reference.has(key)).sort(),
    };
  }
  return result;
}

const ACCENTS: Record<string, string> = {
  a: "à",
  e: "ë",
  i: "ï",
  o: "ô",
  u: "ü",
  A: "À",
  E: "Ë",
  I: "Ï",
  O: "Ô",
  U: "Ü",
};

export function pseudoLocalize(value: string, options: { rtl?: boolean } = {}): string {
  const parts = value.split(/(\{[^{}]+\}|<[^>]+>)/g);
  const transformed = parts
    .map((part) =>
      /^\{[^{}]+\}$|^<[^>]+>$/.test(part)
        ? part
        : part.replace(/[aeiouAEIOU]/g, (character) => ACCENTS[character] ?? character),
    )
    .join("");
  return options.rtl ? `\u202e[${transformed}]\u202c` : `[${transformed}~~~]`;
}

export function createPseudoLocale(messages: Messages, options: { rtl?: boolean } = {}): Messages {
  const output: Messages = Object.create(null) as Messages;
  for (const [key, value] of Object.entries(messages)) {
    output[key] =
      typeof value === "string"
        ? pseudoLocalize(value, options)
        : value && typeof value === "object" && !Array.isArray(value)
          ? createPseudoLocale(value as Messages, options)
          : value;
  }
  return output;
}
