/**
 * Shared utilities for model info code generation.
 */

import * as fs from "node:fs";
import * as path from "node:path";
import { parse as parseYaml } from "yaml";

/**
 * Get the repository root and standard paths for codegen.
 */
export function getPaths(
  provider: string,
  scriptDir: string,
): { repoRoot: string; inputPath: string; outputPath: string } {
  const repoRoot = path.resolve(scriptDir, "../../..");
  return {
    repoRoot,
    inputPath: path.join(
      repoRoot,
      `python/scripts/model_features/data/${provider}.yaml`,
    ),
    outputPath: path.join(
      repoRoot,
      `typescript/src/llm/providers/${provider}/model-info.ts`,
    ),
  };
}

/**
 * Read and parse YAML data from a file.
 */
export function readYamlData<T>(inputPath: string, provider: string): T {
  if (!fs.existsSync(inputPath)) {
    console.error(`Error: Input file not found: ${inputPath}`);
    console.error(
      `Run Python test script first: cd python && uv run python -m scripts.model_features.test_${provider}`,
    );
    process.exit(1);
  }

  console.log(`Reading test results from: ${inputPath}`);
  const yamlContent = fs.readFileSync(inputPath, "utf-8");
  return parseYaml(yamlContent) as T;
}

/**
 * Write generated content to a file, creating directories as needed.
 */
export function writeGeneratedFile(outputPath: string, content: string): void {
  fs.mkdirSync(path.dirname(outputPath), { recursive: true });
  fs.writeFileSync(outputPath, content);
  console.log(`  Generated: ${outputPath}`);
}

/**
 * Generate the file header comment.
 */
export function generateFileHeader(
  providerName: string,
  scriptPath: string,
): string[] {
  return [
    "/**",
    ` * ${providerName} model information.`,
    " *",
    ` * This file is auto-generated by ${scriptPath}`,
    " * Do not edit manually - run `bun run codegen` to update.",
    " */",
  ];
}

/**
 * Generate the known models array, type, and Set exports.
 *
 * Uses the "const array first" pattern where the array is the source of truth
 * and both the type and Set are derived from it.
 *
 * @param providerUpper - Provider name in UPPER_CASE for const names (e.g., "OPENAI")
 * @param providerPascal - Provider name in PascalCase for type names (e.g., "OpenAI")
 * @param modelIds - Array of model IDs to include
 * @param typeDescription - Description for the type JSDoc
 */
export function generateKnownModelsExports(
  providerUpper: string,
  providerPascal: string,
  modelIds: string[],
  typeDescription: string,
): string[] {
  const lines: string[] = [];

  // Generate const array
  lines.push("/**");
  lines.push(` * Array of all known ${providerPascal} model IDs.`);
  lines.push(
    " * This is the source of truth - the type and Set are derived from it.",
  );
  lines.push(" */");
  lines.push(`const ${providerUpper}_KNOWN_MODELS_ARRAY = [`);
  for (const modelId of modelIds) {
    lines.push(`  '${modelId}',`);
  }
  lines.push("] as const;");

  // Derive type from array
  lines.push("");
  lines.push("/**");
  lines.push(` * ${typeDescription}`);
  lines.push(" */");
  lines.push(
    `export type ${providerPascal}KnownModels = (typeof ${providerUpper}_KNOWN_MODELS_ARRAY)[number];`,
  );

  // Export Set derived from array
  lines.push("");
  lines.push("/**");
  lines.push(` * Set of all known ${providerPascal} model IDs.`);
  lines.push(" */");
  lines.push(
    `export const ${providerUpper}_KNOWN_MODELS: ReadonlySet<string> = new Set(${providerUpper}_KNOWN_MODELS_ARRAY);`,
  );

  return lines;
}

/**
 * Generate a feature support Set export.
 */
export function generateFeatureSet(
  constName: string,
  description: string,
  extraDescription: string | null,
  modelIds: string[],
): string[] {
  const lines: string[] = [];

  lines.push("/**");
  lines.push(` * ${description}`);
  if (extraDescription) {
    lines.push(" *");
    lines.push(` * ${extraDescription}`);
  }
  lines.push(" */");
  lines.push(`export const ${constName}: ReadonlySet<string> = new Set([`);
  for (const modelId of modelIds) {
    lines.push(`  '${modelId}',`);
  }
  lines.push("]);");

  return lines;
}
