import { ensureExistingPath, resolveServiceCwd } from "./paths.mjs";
import { parseModuleSpecifier } from "./runtime.mjs";

export function validateConfigCoverage(configs) {
  const names = new Set(configs.map((config) => config.name));
  for (const config of configs) {
    for (const depName of config.testkit.dependsOn || []) {
      if (!names.has(depName)) {
        throw new Error(`Service "${config.name}" depends on "${depName}", but ${depName} is not defined`);
      }
    }
    const databaseFrom = config.testkit.databaseFrom;
    if (databaseFrom && !names.has(databaseFrom)) {
      throw new Error(`Service "${config.name}" databaseFrom "${databaseFrom}" is not defined`);
    }
  }
}

export function validateServiceConfig({
  name,
  local,
  database,
  databaseFrom,
  runtime,
  dependsOn,
  suites,
  productDir,
}) {
  const usesLocalExecution = Object.entries(suites).some(([suiteType, discoveredSuites]) =>
    discoveredSuites.some((suite) => (suite.framework && suite.framework !== "k6") || suiteType !== "dal")
  );

  if (usesLocalExecution && !local) {
    throw new Error(
      `Service "${name}" defines non-DAL suites but no local runtime could be resolved. Add it in testkit.setup.ts.`
    );
  }

  if (database && databaseFrom) {
    throw new Error(`Service "${name}" cannot define both database and databaseFrom`);
  }
  if (runtime.instances < 1) {
    throw new Error(`Service "${name}" runtime.instances must be a positive integer`);
  }
  if (runtime.maxConcurrentTasks <= 0) {
    throw new Error(`Service "${name}" runtime.maxConcurrentTasks must be a positive integer when provided`);
  }

  for (const depName of dependsOn || []) {
    if (depName === name) {
      throw new Error(`Service "${name}" cannot depend on itself`);
    }
  }

  if (local?.cwd) {
    ensureExistingPath(productDir, local.cwd, `Service "${name}" local.cwd`);
  }
  if (runtime.toolchain?.cwd) {
    ensureExistingPath(productDir, runtime.toolchain.cwd, `Service "${name}" runtime.toolchain.cwd`);
  }
  for (const [stageName, steps] of Object.entries(database?.template || {})) {
    if (stageName === "inputs") continue;
    for (const step of steps || []) {
      if (step.cwd) {
        ensureExistingPath(productDir, step.cwd, `Service "${name}" database.template.${stageName} step cwd`);
      }
      if (step.kind === "sql-file") {
        ensureExistingPath(
          resolveServiceCwd(productDir, step.cwd || "."),
          step.path,
          `Service "${name}" database.template.${stageName} sql file`
        );
      }
      if (step.kind === "module") {
        const { modulePath } = parseModuleSpecifier(step.specifier);
        ensureExistingPath(
          resolveServiceCwd(productDir, step.cwd || "."),
          modulePath,
          `Service "${name}" database.template.${stageName} module`
        );
      }
      for (const input of step.inputs || []) {
        ensureExistingPath(
          resolveServiceCwd(productDir, step.cwd || "."),
          input,
          `Service "${name}" database.template.${stageName} step input`
        );
      }
    }
  }
  for (const input of database?.template?.inputs || []) {
    ensureExistingPath(productDir, input, `Service "${name}" database.template input`);
  }
  for (const step of runtime.prepare?.steps || []) {
    if (step.cwd) {
      ensureExistingPath(productDir, step.cwd, `Service "${name}" runtime.prepare step cwd`);
    }
    if (step.kind === "sql-file") {
      ensureExistingPath(
        resolveServiceCwd(productDir, step.cwd || "."),
        step.path,
        `Service "${name}" runtime.prepare sql file`
      );
    }
    if (step.kind === "module") {
      const { modulePath } = parseModuleSpecifier(step.specifier);
      ensureExistingPath(
        resolveServiceCwd(productDir, step.cwd || "."),
        modulePath,
        `Service "${name}" runtime.prepare module`
      );
    }
    for (const input of step.inputs || []) {
      ensureExistingPath(
        resolveServiceCwd(productDir, step.cwd || "."),
        input,
        `Service "${name}" runtime.prepare step input`
      );
    }
  }
  for (const input of runtime.prepare?.inputs || []) {
    ensureExistingPath(productDir, input, `Service "${name}" runtime.prepare input`);
  }
}
