import { appendFile, mkdir, writeFile } from "node:fs/promises";
import path from "node:path";

export type StartupLogLevel = "info" | "warn" | "error";
export type StartupLogPhase = "bootstrap" | "runtime-cloud" | "project-bundle" | "workspace-summary" | "diagnostics";

interface StartupLogEntry {
  ts: string;
  level: StartupLogLevel;
  phase: StartupLogPhase;
  event: string;
  message: string;
  context?: Record<string, unknown>;
}

function toSafeStamp(value: string): string {
  return value.replace(/[:.]/g, "-");
}

function sanitizeUrlLikeString(value: string): string {
  if (!/^https?:\/\//i.test(value)) {
    return value;
  }

  try {
    const parsed = new URL(value);
    return `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
  } catch {
    return value;
  }
}

function sanitizeValue(value: unknown, key?: string): unknown {
  if (value === null || value === undefined) {
    return value;
  }

  if (typeof value === "string") {
    if (key && /(token|secret|api[_-]?key|password)/i.test(key)) {
      return "<redacted>";
    }
    return sanitizeUrlLikeString(value);
  }

  if (Array.isArray(value)) {
    return value.map((item) => sanitizeValue(item));
  }

  if (typeof value === "object") {
    const next: Record<string, unknown> = {};
    for (const [entryKey, entryValue] of Object.entries(value as Record<string, unknown>)) {
      next[entryKey] = sanitizeValue(entryValue, entryKey);
    }
    return next;
  }

  return value;
}

export class StartupLogger {
  private readonly sessionLogPath: string;
  private readonly latestLogPath: string;
  private readonly initPromise: Promise<void>;
  private writeChain: Promise<void> = Promise.resolve();

  constructor(logsPath: string) {
    const startupLogsDir = path.join(logsPath, "startup");
    const stamp = toSafeStamp(new Date().toISOString());
    this.sessionLogPath = path.join(startupLogsDir, `startup-${stamp}.log`);
    this.latestLogPath = path.join(startupLogsDir, "startup-latest.log");
    this.initPromise = mkdir(startupLogsDir, { recursive: true })
      .then(() => Promise.all([
        writeFile(this.sessionLogPath, "", "utf8"),
        writeFile(this.latestLogPath, "", "utf8")
      ]))
      .then(() => undefined);
  }

  getSessionLogPath(): string {
    return this.sessionLogPath;
  }

  info(phase: StartupLogPhase, event: string, message: string, context?: Record<string, unknown>): Promise<void> {
    return this.log("info", phase, event, message, context);
  }

  warn(phase: StartupLogPhase, event: string, message: string, context?: Record<string, unknown>): Promise<void> {
    return this.log("warn", phase, event, message, context);
  }

  error(phase: StartupLogPhase, event: string, message: string, context?: Record<string, unknown>): Promise<void> {
    return this.log("error", phase, event, message, context);
  }

  async log(level: StartupLogLevel, phase: StartupLogPhase, event: string, message: string, context?: Record<string, unknown>): Promise<void> {
    const entry: StartupLogEntry = {
      ts: new Date().toISOString(),
      level,
      phase,
      event,
      message,
      ...(context ? { context: sanitizeValue(context) as Record<string, unknown> } : {})
    };
    const line = JSON.stringify(entry) + "`n";

    this.writeChain = this.writeChain.then(async () => {
      await this.initPromise;
      await appendFile(this.sessionLogPath, line, "utf8");
      await appendFile(this.latestLogPath, line, "utf8");
    }).catch(() => undefined);

    await this.writeChain;
  }
}
