import { mkdir, open, rename, stat } from "node:fs/promises";
import { join } from "node:path";

const SECRET_PATTERNS: RegExp[] = [
  /(?:access|refresh|identity|xsts|xbox)[_-]?token\s*[=:]\s*[^\s,;]+/gi,
  /Bearer\s+[A-Za-z0-9._~+\/-]+=*/gi,
];
const JWT_PATTERN = /[A-Za-z0-9_-]{20,4096}\.[A-Za-z0-9_-]{20,4096}\.[A-Za-z0-9_-]{20,4096}/g;

export function redact(value: unknown): string {
  let output = value instanceof Error ? `${value.name}: ${value.message}\n${value.stack ?? ""}` : String(value);
  for (const pattern of SECRET_PATTERNS) output = output.replace(pattern, "[SECRET MASQUÉ]");
  // Avoid expensive token-shape scanning for ordinary long game log lines.
  if (output.indexOf(".") !== -1 && output.indexOf(".", output.indexOf(".") + 1) !== -1) {
    output = output.replace(JWT_PATTERN, "[SECRET MASQUÉ]");
  }
  return output.slice(0, 32_000);
}

export class LauncherLogger {
  readonly file: string;

  constructor(private readonly directory: string) {
    this.file = join(directory, "launcher.log");
  }

  async initialize(): Promise<void> {
    await mkdir(this.directory, { recursive: true, mode: 0o700 });
    try {
      const metadata = await stat(this.file);
      if (metadata.size > 5 * 1024 * 1024) await rename(this.file, join(this.directory, "launcher.previous.log"));
    } catch (error) {
      if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
    }
  }

  async write(level: "INFO" | "WARN" | "ERROR", message: string, detail?: unknown): Promise<void> {
    const suffix = detail === undefined ? "" : ` | ${redact(detail)}`;
    const line = `${new Date().toISOString()} ${level} ${redact(message)}${suffix}\n`;
    const handle = await open(this.file, "a", 0o600);
    try {
      await handle.write(line);
    } finally {
      await handle.close();
    }
  }

  info(message: string, detail?: unknown): Promise<void> { return this.write("INFO", message, detail); }
  warn(message: string, detail?: unknown): Promise<void> { return this.write("WARN", message, detail); }
  error(message: string, detail?: unknown): Promise<void> { return this.write("ERROR", message, detail); }
}
