import fs from 'node:fs';
import path from 'node:path';

import { PIPELINE_DIR, STAGE_SEQUENCE, STAGE_SPECS, STAGE_TIMEOUTS } from './constants.js';
import type { ForgeTask } from './types.js';

/**
 * Generate a deterministic task ID for a stage within a run.
 */
export function stageTaskId(runId: string, stageName: string): string {
  const spec = STAGE_SPECS[stageName];
  if (!spec) throw new Error(`Unknown Forge stage: ${stageName}`);
  return `FORGE-${runId}-${spec.number}`;
}

/**
 * Get the directory for a stage's artifacts within a run.
 */
export function stageDir(runDir: string, stageName: string): string {
  return path.join(runDir, stageName);
}

/**
 * Get the brief path for a stage within a run.
 */
export function stageBriefPath(runDir: string, stageName: string): string {
  return path.join(stageDir(runDir, stageName), 'brief.md');
}

/**
 * Get the result path for a stage within a run.
 */
export function stageResultPath(runDir: string, stageName: string): string {
  return path.join(stageDir(runDir, stageName), 'result.json');
}

/**
 * Load a stage prompt from the pipeline assets.
 */
export function loadStagePrompt(promptFile: string): string {
  const promptPath = path.join(PIPELINE_DIR, 'stages', promptFile);
  return fs.readFileSync(promptPath, 'utf-8').trim();
}

/**
 * Build the brief content for a stage, combining source brief with stage definition.
 */
export function buildStageBrief(opts: {
  stageName: string;
  stagePrompt: string;
  briefContent: string;
  projectRoot: string;
  runId: string;
  runDir: string;
}): string {
  return [
    `# Forge Pipeline Stage: ${opts.stageName}`,
    '',
    `Run ID: ${opts.runId}`,
    `Project Root: ${opts.projectRoot}`,
    '',
    '## Source Brief',
    opts.briefContent.trim(),
    '',
    `Read previous stage results from ${opts.runDir}/ before proceeding.`,
    '',
    '## Stage Definition',
    opts.stagePrompt,
    '',
  ].join('\n');
}

/**
 * Write the stage brief to disk and return the path.
 */
export function writeStageBrief(opts: {
  stageName: string;
  briefContent: string;
  projectRoot: string;
  runId: string;
  runDir: string;
}): string {
  const spec = STAGE_SPECS[opts.stageName];
  if (!spec) throw new Error(`Unknown Forge stage: ${opts.stageName}`);

  const briefPath = stageBriefPath(opts.runDir, opts.stageName);
  fs.mkdirSync(path.dirname(briefPath), { recursive: true });

  const stagePrompt = loadStagePrompt(spec.promptFile);
  const content = buildStageBrief({
    stageName: opts.stageName,
    stagePrompt,
    briefContent: opts.briefContent,
    projectRoot: opts.projectRoot,
    runId: opts.runId,
    runDir: opts.runDir,
  });

  fs.writeFileSync(briefPath, content, 'utf-8');
  return briefPath;
}

/**
 * Convert a Forge stage into a ForgeTask ready for submission to a TaskExecutor.
 */
export function mapStageToTask(opts: {
  stageName: string;
  briefContent: string;
  projectRoot: string;
  runId: string;
  runDir: string;
}): ForgeTask {
  const { stageName, briefContent, projectRoot, runId, runDir } = opts;

  const spec = STAGE_SPECS[stageName];
  if (!spec) throw new Error(`Unknown Forge stage: ${stageName}`);

  const timeout = STAGE_TIMEOUTS[stageName];
  if (timeout === undefined) {
    throw new Error(`Missing stage timeout for Forge stage: ${stageName}`);
  }

  const briefPath = writeStageBrief({
    stageName,
    briefContent,
    projectRoot,
    runId,
    runDir,
  });
  const resultPath = stageResultPath(runDir, stageName);
  const taskId = stageTaskId(runId, stageName);
  const promptPath = path.join(PIPELINE_DIR, 'stages', spec.promptFile);

  const task: ForgeTask = {
    id: taskId,
    title: spec.title,
    description: `Forge stage ${stageName} via MACP`,
    status: 'pending',
    dispatch: spec.dispatch,
    type: spec.type,
    briefPath: path.resolve(briefPath),
    resultPath: path.resolve(resultPath),
    timeoutSeconds: timeout,
    qualityGates: [...spec.qualityGates],
    metadata: {
      runId,
      runDir,
      stageName,
      stageNumber: spec.number,
      gate: spec.gate,
      promptPath: path.resolve(promptPath),
      resultOutputPath: path.resolve(resultPath),
    },
  };

  // Build dependency chain from stage sequence
  const stageIndex = STAGE_SEQUENCE.indexOf(stageName);
  if (stageIndex > 0) {
    const prevStage = STAGE_SEQUENCE[stageIndex - 1]!;
    task.dependsOn = [stageTaskId(runId, prevStage)];
  }

  // exec dispatch stages get a worktree reference
  if (spec.dispatch === 'exec') {
    task.worktree = path.resolve(projectRoot);
  }

  return task;
}
