import { promises as fs } from "node:fs";

export type TextEncodingState = "verified" | "invalid" | "unavailable";

export interface TextEncodingCheck {
  state: TextEncodingState;
  code: "ENCODING_UTF8" | "ENCODING_INVALID" | "ENCODING_UNAVAILABLE";
  findings: string[];
}

const MOJIBAKE_PATTERNS: Array<{ code: string; pattern: RegExp }> = [
  { code: "replacement_character", pattern: /\uFFFD/ },
  { code: "question_mark_run", pattern: /\?{3,}/ },
  { code: "utf8_as_latin1", pattern: /(?:Ã.|Â.|â€|â€™|â€œ|â€�|ï¿½)/ },
  { code: "double_decoded_gbk", pattern: /(?:锟斤拷|烫烫烫|屯屯屯)/ },
];

export function inspectUtf8Text(text: string): TextEncodingCheck {
  const findings = MOJIBAKE_PATTERNS
    .filter((entry) => entry.pattern.test(text))
    .map((entry) => entry.code);
  return findings.length
    ? { state: "invalid", code: "ENCODING_INVALID", findings }
    : { state: "verified", code: "ENCODING_UTF8", findings: [] };
}

export function decodeUtf8Strict(bytes: Uint8Array): {
  text?: string;
  check: TextEncodingCheck;
} {
  try {
    const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
    return { text, check: inspectUtf8Text(text) };
  } catch {
    return {
      check: {
        state: "invalid",
        code: "ENCODING_INVALID",
        findings: ["strict_utf8_decode_failed"],
      },
    };
  }
}

export async function inspectUtf8File(path: string): Promise<TextEncodingCheck> {
  try {
    const bytes = await fs.readFile(path);
    return decodeUtf8Strict(bytes).check;
  } catch (error) {
    return {
      state: "unavailable",
      code: "ENCODING_UNAVAILABLE",
      findings: [error instanceof Error ? error.message : String(error)],
    };
  }
}
