#!/usr/bin/env node
/**
 * Postinstall Script
 *
 * 1. Verifies that the platform-specific daemon binary is available
 * 2. Assembles the Ghostflow.app bundle for proper Login Items display
 * 3. Registers the app with Launch Services
 */

import { execSync } from "node:child_process";
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import { fileURLToPath } from "node:url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const YELLOW = "\x1b[33m";
const GREEN = "\x1b[32m";
const DIM = "\x1b[2m";
const RESET = "\x1b[0m";

// ============================================================================
// Platform Detection
// ============================================================================

/**
 * Get the binary package name for the current platform.
 * @returns {{ supported: boolean, platform?: string, arch?: string, packageName?: string }}
 */
const getBinaryPackageName = () => {
  const platform = os.platform();
  const arch = os.arch();

  if (platform !== "darwin") {
    return { supported: false, platform, arch };
  }

  if (arch === "arm64") {
    return { supported: true, packageName: "@ghostflow/daemon-darwin-arm64" };
  }
  if (arch === "x64") {
    return { supported: true, packageName: "@ghostflow/daemon-darwin-x64" };
  }

  return { supported: false, platform, arch };
};

// ============================================================================
// Binary Discovery
// ============================================================================

/**
 * Build binary info from a search path.
 * @param {string} searchPath
 * @returns {{ binaryPath: string, resourcesPath: string }}
 */
const toBinaryInfo = (searchPath) => ({
  binaryPath: path.join(searchPath, "bin", "ghostflow-daemon"),
  resourcesPath: path.join(searchPath, "resources"),
});

/**
 * Check if the binary package is installed and has the binary.
 * @param {string} packageName
 * @returns {{ found: boolean, binaryPath?: string, resourcesPath?: string }}
 */
const findBinaryPackage = (packageName) => {
  const searchPaths = [
    // Relative to this package
    path.join(__dirname, "..", "node_modules", packageName),
    // Monorepo root node_modules
    path.join(__dirname, "..", "..", "..", "node_modules", packageName),
  ];

  const found = searchPaths
    .map(toBinaryInfo)
    .find(({ binaryPath }) => fs.existsSync(binaryPath));

  if (found === undefined) {
    return { found: false };
  }

  return {
    found: true,
    binaryPath: found.binaryPath,
    resourcesPath: fs.existsSync(found.resourcesPath) ? found.resourcesPath : undefined,
  };
};

/**
 * Check for local development binary and resources.
 * @returns {{ found: boolean, binaryPath?: string, resourcesPath?: string }}
 */
const findLocalBinary = () => {
  const daemonDir = path.join(__dirname, "..", "..", "daemon");
  const searchPaths = [
    path.join(daemonDir, ".build", "release", "ghostflow-daemon"),
    path.join(daemonDir, ".build", "debug", "ghostflow-daemon"),
  ];

  const foundPath = searchPaths.find((p) => fs.existsSync(p));

  if (foundPath === undefined) {
    return { found: false };
  }

  const resourcesPath = path.join(daemonDir, "Resources");
  return {
    found: true,
    binaryPath: foundPath,
    resourcesPath: fs.existsSync(resourcesPath) ? resourcesPath : undefined,
  };
};

// ============================================================================
// Version Checking
// ============================================================================

/**
 * Check if source binary is newer than installed binary.
 * @param {string} sourcePath
 * @param {string} installedPath
 * @returns {boolean}
 */
const isSourceNewer = (sourcePath, installedPath) => {
  if (!fs.existsSync(installedPath)) {
    return true; // No installed version, always install
  }

  try {
    const sourceStat = fs.statSync(sourcePath);
    const installedStat = fs.statSync(installedPath);
    return sourceStat.mtimeMs > installedStat.mtimeMs;
  } catch {
    return true; // On error, assume we should update
  }
};

// ============================================================================
// App Bundle Assembly
// ============================================================================

/**
 * Assemble the Ghostflow.app bundle in Application Support.
 * @param {string} binaryPath
 * @param {string | undefined} resourcesPath
 * @returns {{ appPath: string, executablePath: string, updated: boolean }}
 */
const assembleAppBundle = (binaryPath, resourcesPath) => {
  const appSupportDir = path.join(os.homedir(), "Library", "Application Support", "ghostflow");
  const appPath = path.join(appSupportDir, "Ghostflow.app");
  const contentsPath = path.join(appPath, "Contents");
  const macOSPath = path.join(contentsPath, "MacOS");
  const appResourcesPath = path.join(contentsPath, "Resources");

  const destBinaryPath = path.join(macOSPath, "ghostflow-daemon");

  // Check if update is needed
  if (!isSourceNewer(binaryPath, destBinaryPath)) {
    return { appPath, executablePath: destBinaryPath, updated: false };
  }

  // Create directory structure
  fs.mkdirSync(macOSPath, { recursive: true });
  fs.mkdirSync(appResourcesPath, { recursive: true });

  // Copy binary
  fs.copyFileSync(binaryPath, destBinaryPath);
  fs.chmodSync(destBinaryPath, 0o755);

  // Copy resources if available
  if (resourcesPath) {
    const infoPlistSrc = path.join(resourcesPath, "Info.plist");
    const pkgInfoSrc = path.join(resourcesPath, "PkgInfo");
    const iconSrc = path.join(resourcesPath, "AppIcon.icns");

    if (fs.existsSync(infoPlistSrc)) {
      fs.copyFileSync(infoPlistSrc, path.join(contentsPath, "Info.plist"));
    }

    if (fs.existsSync(pkgInfoSrc)) {
      fs.copyFileSync(pkgInfoSrc, path.join(contentsPath, "PkgInfo"));
    }

    if (fs.existsSync(iconSrc)) {
      fs.copyFileSync(iconSrc, path.join(appResourcesPath, "AppIcon.icns"));
    }
  }

  return { appPath, executablePath: destBinaryPath, updated: true };
};

/**
 * Register the app bundle with Launch Services.
 * @param {string} appPath
 * @returns {boolean}
 */
const registerWithLaunchServices = (appPath) => {
  const lsregister =
    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister";

  if (!fs.existsSync(lsregister)) {
    return false;
  }

  try {
    execSync(`"${lsregister}" -f "${appPath}"`, { stdio: "ignore" });
    return true;
  } catch (err) {
    console.log(`${DIM}  lsregister failed: ${err.message}${RESET}`);
    return false;
  }
};

// ============================================================================
// Main
// ============================================================================

const main = () => {
  console.log(`${GREEN}Ghostflow CLI${RESET} installed successfully!`);
  console.log();

  const platformInfo = getBinaryPackageName();

  if (!platformInfo.supported) {
    console.log(`${YELLOW}⚠ Warning:${RESET} Ghostflow currently only supports macOS.`);
    console.log(`  Your platform: ${platformInfo.platform}-${platformInfo.arch}`);
    console.log();
    console.log("  You can still use the CLI, but you'll need to provide");
    console.log("  a custom daemon binary path with --daemon-path.");
    return;
  }

  // Try to find binary (published package first, then local dev)
  let binaryInfo = findBinaryPackage(platformInfo.packageName);

  if (!binaryInfo.found) {
    binaryInfo = findLocalBinary();
  }

  if (!binaryInfo.found) {
    console.log(`${YELLOW}⚠ Note:${RESET} Daemon binary not found.`);
    console.log();
    console.log("  The optional binary package was not installed.");
    console.log("  This is normal if the package hasn't been published yet.");
    console.log();
    console.log("  For development:");
    console.log("    cd packages/daemon && swift build -c release");
    console.log();
    console.log("  Then start the CLI:");
    console.log("    ghostflow start");
    return;
  }

  console.log(`${GREEN}✓${RESET} Daemon binary found`);
  console.log(`  ${DIM}${binaryInfo.binaryPath}${RESET}`);

  // Assemble app bundle for proper Login Items display
  if (binaryInfo.resourcesPath) {
    try {
      const { appPath, updated } = assembleAppBundle(
        binaryInfo.binaryPath,
        binaryInfo.resourcesPath,
      );

      if (updated) {
        console.log(`${GREEN}✓${RESET} App bundle assembled`);
        console.log(`  ${DIM}${appPath}${RESET}`);

        // Register with Launch Services
        if (registerWithLaunchServices(appPath)) {
          console.log(`${GREEN}✓${RESET} Registered with Launch Services`);
        }
      } else {
        console.log(`${GREEN}✓${RESET} App bundle up to date`);
        console.log(`  ${DIM}${appPath}${RESET}`);
      }
    } catch (err) {
      console.log(`${YELLOW}⚠${RESET} Could not assemble app bundle: ${err.message}`);
    }
  } else {
    console.log(`${YELLOW}⚠${RESET} Resources not found, skipping app bundle assembly`);
  }

  console.log();
  console.log("  Get started:");
  console.log("    ghostflow start    # Start the daemon");
  console.log("    ghostflow status   # Check daemon status");
  console.log("    ghostflow --help   # View all commands");
};

main();
