import fs from "fs";
import path from "path";
import { execa } from "execa";
import { buildExecutionEnv } from "../runner/template.mjs";
import {
  collectConfiguredInputs,
  runConfiguredSteps,
} from "../runner/template-steps.mjs";
import { captureOutput } from "../runner/processes.mjs";

export async function runTemplateStage(config, stageName, databaseUrl, options = {}) {
  const steps = config.testkit.database?.template?.[stageName] || [];
  if (steps.length === 0) return;

  const env = {
    ...buildExecutionEnv(config, {}, process.env),
    DATABASE_URL: databaseUrl,
  };

  await runConfiguredSteps({
    config,
    steps,
    env,
    labelPrefix: `template:${stageName}`,
    reporter: options.reporter || null,
    setupRegistry: options.setupRegistry || null,
    parentOperation: options.parentOperation || null,
  });
}

export function collectTemplateInputs(productDir, template = {}) {
  const steps = ["migrate", "seed", "verify"].flatMap((stageName) => template[stageName] || []);
  return collectConfiguredInputs(productDir, {
    inputs: template.inputs || [],
    steps,
  });
}

export async function captureTemplateSnapshot(config, outputPath, databaseUrl, options = {}) {
  const templateDbUrl = databaseUrl;
  const absoluteOutputPath = path.resolve(config.productDir, outputPath);
  fs.mkdirSync(path.dirname(absoluteOutputPath), { recursive: true });

  const child = execa(
    "pg_dump",
    [
      "--schema-only",
      "--no-owner",
      "--no-privileges",
      "--file",
      absoluteOutputPath,
      templateDbUrl,
    ],
    {
      cwd: config.productDir,
      env: {
        ...buildExecutionEnv(config, {}, process.env),
        DATABASE_URL: templateDbUrl,
      },
      stdout: "pipe",
      stderr: "pipe",
      reject: false,
    }
  );
  const liveWriter =
    options.reporter?.outputMode === "debug"
      ? (line) => options.reporter.writeDebugLine?.(line)
      : null;
  const logRecord = options.logRecord || null;
  const drains = [
    captureOutput(child.stdout, {
      livePrefix: `[${config.runtimeLabel || config.name}:${config.name}]`,
      liveWriter,
      onLine(line) {
        if (logRecord) logRecord.stream.write(`${new Date().toISOString()} [stdout] ${line}\n`);
      },
    }),
    captureOutput(child.stderr, {
      livePrefix: `[${config.runtimeLabel || config.name}:${config.name}]`,
      liveWriter,
      onLine(line) {
        if (logRecord) logRecord.stream.write(`${new Date().toISOString()} [stderr] ${line}\n`);
      },
    }),
  ];
  const result = await child;
  await Promise.all(drains);
  if (result.exitCode !== 0) {
    throw new Error(result.shortMessage || result.stderr || result.stdout || "pg_dump failed");
  }

  sanitizeSnapshotFile(absoluteOutputPath);
  return absoluteOutputPath;
}

export function sanitizeSnapshotFile(filePath) {
  const dump = fs.readFileSync(filePath, "utf8");
  const sanitized = dump
    .split("\n")
    .filter((line) => {
      const trimmed = line.trim();
      return (
        trimmed !== "SET transaction_timeout = 0;" &&
        !trimmed.startsWith("\\restrict ") &&
        !trimmed.startsWith("\\unrestrict ")
      );
    })
    .join("\n");

  if (sanitized !== dump) {
    fs.writeFileSync(filePath, sanitized);
  }
}
