import * as fs from "node:fs/promises";
import * as path from "node:path";
import { getSDKVersions } from "./utils";

const LOG_DIR = path.join(process.cwd(), ".embeddable", "logs");
export const ERROR_LOG_FILE = path.join(LOG_DIR, "error.log");
const MAX_LOG_SIZE = 5 * 1024 * 1024; // 5 MB
const MAX_LOG_FILES = 5;

interface LogEntry {
  timestamp: string;
  command: string;
  breadcrumbs: string[];
  error: string;
}

interface LogErrorParams {
  command: string;
  breadcrumbs: string[];
  error: unknown;
}

export async function initLogger(command: string) {
  try {
    await fs.mkdir(LOG_DIR, { recursive: true });
  } catch (error) {
    console.error("Failed to create log directory:", error);
  }

  setupGlobalErrorHandlers(command);
}

export async function logError({
  command,
  breadcrumbs,
  error,
}: LogErrorParams) {
  const sdkVersions = getSDKVersions();
  const logEntry: LogEntry = {
    timestamp: new Date().toISOString(),
    command,
    breadcrumbs,
    error:
      error instanceof Error
        ? `${error.name}: ${error.message}\n${error.stack}`
        : String(error),
  };

  const logMessage = `
[${logEntry.timestamp}] Command: ${logEntry.command}
Breadcrumbs: ${logEntry.breadcrumbs.join(" > ")}
Error: ${logEntry.error}
OS: ${process.platform}
Node: ${process.version}
SDK Versions: ${JSON.stringify(sdkVersions, null, 2)}
----------------------------------------
`;

  try {
    await rotateLogIfNeeded();
    await fs.appendFile(ERROR_LOG_FILE, logMessage);
    console.error(
      `An error occurred during ${command}. Check the log file for details: ${ERROR_LOG_FILE}`,
    );
  } catch (error) {
    console.error("Failed to write to log file:", error);
  }
}

async function rotateLogIfNeeded() {
  try {
    const stats = await fs.stat(ERROR_LOG_FILE);
    if (stats.size < MAX_LOG_SIZE) {
      return;
    }

    for (let i = MAX_LOG_FILES - 1; i > 0; i--) {
      const oldFile = `${ERROR_LOG_FILE}.${i}`;
      const newFile = `${ERROR_LOG_FILE}.${i + 1}`;
      try {
        await fs.rename(oldFile, newFile);
      } catch (error) {
        // Ignore error if file doesn't exist
      }
    }

    await fs.rename(ERROR_LOG_FILE, `${ERROR_LOG_FILE}.1`);
    await fs.writeFile(ERROR_LOG_FILE, ""); // Create a new empty log file
  } catch (error: any) {
    if (error.code !== "ENOENT") {
      console.error("Error rotating log file:", error);
    }
  }
}

function setupGlobalErrorHandlers(command: string) {
  process.on("uncaughtException", async (error) => {
    await logError({ command, breadcrumbs: ["uncaughtException"], error });
    console.error(error);
    process.exit(1);
  });

  process.on("unhandledRejection", async (reason) => {
    await logError({
      command,
      breadcrumbs: ["unhandledRejection"],
      error: reason as Error | string,
    });
    console.error(reason);
    process.exit(1);
  });
}
