import {
  cpSync,
  existsSync,
  readFileSync,
  renameSync,
  rmSync,
  writeFileSync,
} from "node:fs";
import path from "node:path";
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";

const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");

/** User-facing Windows app entry point (Electrobun launcher). */
export const WINDOWS_APP_EXECUTABLE = "farmcontrol-server.exe";

/**
 * Bun runtime filename embedded in the Electrobun launcher binary.
 * Must stay exactly 7 characters (same length as "bun.exe") for binary patching.
 */
export const WINDOWS_RUNTIME_EXECUTABLE = "farm.exe";

const LAUNCHER_BINARY = "launcher.exe";
const LEGACY_RUNTIME_BINARY = "bun.exe";
const LAUNCHER_RUNTIME_REFERENCE = Buffer.from(`${LEGACY_RUNTIME_BINARY}\0`);
const PATCHED_RUNTIME_REFERENCE = Buffer.from(`${WINDOWS_RUNTIME_EXECUTABLE}\0`);

function ensureIconIco() {
  const iconPath = path.join(rootDir, "assets/icon.ico");
  if (existsSync(iconPath)) {
    return iconPath;
  }

  const sources = [
    path.join(rootDir, "assets/farmcontrolhosticon.png"),
    path.join(rootDir, "assets/icon.png"),
  ];
  const source = sources.find((candidate) => existsSync(candidate));
  if (!source) {
    throw new Error(
      "prepare-windows-app-binaries: assets/icon.ico not found and no source PNG to generate it",
    );
  }

  console.log(`prepare-windows-app-binaries: generating icon.ico from ${source}`);
  const result = spawnSync(
    "bun",
    [path.join(rootDir, "scripts/prepare-app-icons.mjs"), source],
    { cwd: rootDir, stdio: "inherit", env: process.env },
  );

  if (result.status !== 0 || !existsSync(iconPath)) {
    throw new Error("prepare-windows-app-binaries: failed to generate assets/icon.ico");
  }

  return iconPath;
}

function patchLauncherRuntimeReference(launcherPath) {
  const binary = readFileSync(launcherPath);
  const index = binary.indexOf(LAUNCHER_RUNTIME_REFERENCE);
  if (index === -1) {
    if (binary.includes(PATCHED_RUNTIME_REFERENCE)) {
      return;
    }

    throw new Error(
      `prepare-windows-app-binaries: could not find ${LEGACY_RUNTIME_BINARY} reference in ${launcherPath}`,
    );
  }

  PATCHED_RUNTIME_REFERENCE.copy(binary, index);
  writeFileSync(launcherPath, binary);
  console.log(
    `prepare-windows-app-binaries: patched ${path.basename(launcherPath)} to spawn ${WINDOWS_RUNTIME_EXECUTABLE}`,
  );
}

function renameWindowsRuntime(binDir) {
  const legacyRuntimePath = path.join(binDir, LEGACY_RUNTIME_BINARY);
  const runtimePath = path.join(binDir, WINDOWS_RUNTIME_EXECUTABLE);

  if (existsSync(runtimePath)) {
    return runtimePath;
  }

  if (!existsSync(legacyRuntimePath)) {
    throw new Error(
      `prepare-windows-app-binaries: neither ${LEGACY_RUNTIME_BINARY} nor ${WINDOWS_RUNTIME_EXECUTABLE} found in ${binDir}`,
    );
  }

  renameSync(legacyRuntimePath, runtimePath);
  console.log(
    `prepare-windows-app-binaries: renamed ${LEGACY_RUNTIME_BINARY} -> ${WINDOWS_RUNTIME_EXECUTABLE}`,
  );
  return runtimePath;
}

function createBrandedAppExecutable(binDir) {
  const launcherPath = path.join(binDir, LAUNCHER_BINARY);
  const appExecutablePath = path.join(binDir, WINDOWS_APP_EXECUTABLE);

  if (!existsSync(launcherPath)) {
    if (existsSync(appExecutablePath)) {
      patchLauncherRuntimeReference(appExecutablePath);
      return appExecutablePath;
    }

    throw new Error(
      `prepare-windows-app-binaries: ${LAUNCHER_BINARY} not found in ${binDir}`,
    );
  }

  patchLauncherRuntimeReference(launcherPath);
  cpSync(launcherPath, appExecutablePath);
  rmSync(launcherPath, { force: true });
  console.log(
    `prepare-windows-app-binaries: created ${WINDOWS_APP_EXECUTABLE} and removed ${LAUNCHER_BINARY}`,
  );
  return appExecutablePath;
}

async function applyWindowsExecutableMetadata(exePath, iconPath) {
  const rcedit = (await import("rcedit")).default;
  await rcedit(exePath, {
    icon: iconPath,
    "version-string": {
      CompanyName: "Tom Butcher",
      FileDescription: "Farm Control Server",
      ProductName: "Farm Control Server",
      OriginalFilename: path.basename(exePath),
    },
  });
}

/**
 * Brand Windows app binaries for install: farmcontrol-server.exe entry point,
 * farm.exe runtime (patched launcher reference), and embedded icons.
 */
export async function prepareWindowsAppBinaries(appDir) {
  if (process.platform !== "win32") {
    console.log("prepare-windows-app-binaries: skipped (not Windows)");
    return;
  }

  const binDir = path.join(appDir, "bin");
  if (!existsSync(binDir)) {
    throw new Error(`prepare-windows-app-binaries: bin directory not found: ${binDir}`);
  }

  const iconPath = ensureIconIco();
  const runtimePath = renameWindowsRuntime(binDir);
  const appExecutablePath = createBrandedAppExecutable(binDir);

  for (const exePath of [appExecutablePath, runtimePath]) {
    console.log(`prepare-windows-app-binaries: embedding icon into ${exePath}`);
    try {
      await applyWindowsExecutableMetadata(exePath, iconPath);
    } catch (error) {
      const message = error instanceof Error ? error.message : String(error);
      throw new Error(
        `prepare-windows-app-binaries: failed to update ${path.basename(exePath)}: ${message}`,
      );
    }
  }
}
