// src/loader.ts
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
// Licensed under the Apache License, Version 2.0

/// <reference types="node" />

import fs from "node:fs";
import path from "node:path";
import os from "node:os";
import yaml from "js-yaml";
import type { GadgetCodeConfig, GadgetDroneConfig } from "./types.js";

/**
 * Substitute environment variables in YAML values.
 * Supports ${VAR_NAME} syntax.
 */
function substituteEnvVariables(value: string): string {
  return value.replace(/\$\{([^}]+)\}/g, (_match, envVar) => {
    const envValue = process.env[envVar];
    if (envValue === undefined) {
      throw new Error(
        `Environment variable ${envVar} is not set but referenced in config`,
      );
    }
    return envValue;
  });
}

/**
 * Recursively process config object to substitute environment variables
 */
function processConfigValues(obj: unknown): unknown {
  if (typeof obj === "string") {
    return substituteEnvVariables(obj);
  }
  if (Array.isArray(obj)) {
    return obj.map((item) => processConfigValues(item));
  }
  if (obj !== null && typeof obj === "object") {
    const result: Record<string, unknown> = {};
    for (const [key, value] of Object.entries(obj)) {
      result[key] = processConfigValues(value);
    }
    return result;
  }
  return obj;
}

/**
 * Search for configuration file in standard locations.
 * Order: ~/.config/gadget/ first, then /etc/gadget/
 */
export function searchConfigFile(configName: string): {
  path: string;
  exists: boolean;
} {
  const homeConfigDir = path.join(os.homedir(), ".config", "gadget");
  const systemConfigDir = "/etc/gadget";

  const homeConfigPath = path.join(homeConfigDir, configName);
  const systemConfigPath = path.join(systemConfigDir, configName);

  // Check home directory first (user config takes precedence)
  if (fs.existsSync(homeConfigPath)) {
    return { path: homeConfigPath, exists: true };
  }

  // Check system directory
  if (fs.existsSync(systemConfigPath)) {
    return { path: systemConfigPath, exists: true };
  }

  // Not found
  return { path: homeConfigPath, exists: false };
}

/**
 * Load and parse YAML configuration file.
 * Throws descriptive error if file not found.
 */
export function loadYamlConfig<T>(configName: string): T {
  const result = searchConfigFile(configName);

  if (!result.exists) {
    const error = new Error(
      `Configuration file not found: ${configName}\n\n` +
        `Searched in:\n` +
        `  - ${path.join(os.homedir(), ".config", "gadget", configName)}\n` +
        `  - /etc/gadget/${configName}\n\n` +
        `Please create a configuration file. See documentation at:\n` +
        `  ./docs/configuration.md`,
    );
    error.name = "ConfigNotFoundError";
    throw error;
  }

  try {
    const content = fs.readFileSync(result.path, "utf-8");
    const parsed = yaml.load(content) as Record<string, unknown>;
    const processed = processConfigValues(parsed) as T;
    return processed;
  } catch (error) {
    const err = error as Error;
    throw new Error(
      `Failed to parse configuration file ${result.path}: ${err.message}`,
    );
  }
}

/**
 * Load Gadget Code configuration
 */
export function loadGadgetCodeConfig(): GadgetCodeConfig {
  return loadYamlConfig<GadgetCodeConfig>("gadget-code.yaml");
}

/**
 * Load Gadget Drone configuration
 */
export function loadGadgetDroneConfig(): GadgetDroneConfig {
  return loadYamlConfig<GadgetDroneConfig>("gadget-drone.yaml");
}

/**
 * Resolve a path that may contain ~ for home directory
 */
export function resolvePath(filePath: string): string {
  if (filePath.startsWith("~/")) {
    return path.join(os.homedir(), filePath.slice(2));
  }
  return filePath;
}
