import path from "path";
import { normalizeTypeValues, parseSuiteSelectors } from "../runner/suite-selection.mjs";
import {
  parseFileTimeoutOption,
  parseWorkersOption,
} from "../runner/execution-config.mjs";

export const POSITIONAL_TYPES = new Set(["int", "e2e", "dal", "load", "pw", "all"]);
export const LIFECYCLE = new Set(["status", "destroy", "cleanup"]);
export const KNOWN_FAILURES_ACTIONS = new Set(["render", "validate"]);
export const RESERVED = new Set([...POSITIONAL_TYPES, ...LIFECYCLE]);

export function resolveCliSelection({ first, second, third }) {
  let lifecycle = null;
  let positionalType = null;
  let knownFailuresAction = null;
  let dbAction = null;

  if (first === "known-failures") {
    if (!second || !KNOWN_FAILURES_ACTIONS.has(second) || third) {
      throw new Error(
        'Unknown known-failures command. Expected "known-failures render" or "known-failures validate".'
      );
    }
    knownFailuresAction = second;
    return { lifecycle, positionalType, knownFailuresAction, dbAction };
  }

  if (first === "db") {
    if (second === "snapshot" && third === "capture") {
      dbAction = "snapshot-capture";
      return { lifecycle, positionalType, knownFailuresAction, dbAction };
    }
    throw new Error('Unknown db command. Expected "db snapshot capture".');
  }

  if (second || third) {
    throw new Error(`Unexpected extra positional arguments. Use --service and --type instead.`);
  }

  if (first && LIFECYCLE.has(first)) {
    lifecycle = first;
  } else if (first && POSITIONAL_TYPES.has(first)) {
    positionalType = first;
  } else if (first) {
    throw new Error(
      `Unknown argument "${first}". Expected a lifecycle command (status, destroy, cleanup) ` +
      `or suite type (int, e2e, dal, load, pw, all).`
    );
  }

  return { lifecycle, positionalType, knownFailuresAction, dbAction };
}

export function parseTypeOption(values, positionalType = null) {
  const input = [];
  if (positionalType) input.push(positionalType);
  if (Array.isArray(values)) input.push(...values);
  else if (values) input.push(values);
  return normalizeTypeValues(input);
}

export function parseSuiteOption(values) {
  const input = Array.isArray(values) ? values : values ? [values] : [];
  return parseSuiteSelectors(input);
}

export {
  parseFileTimeoutOption,
  parseWorkersOption,
};

export function parseShardOption(value) {
  if (!value) return null;

  const match = String(value).match(/^(\d+)\/(\d+)$/);
  if (!match) {
    throw new Error(
      `Invalid --shard value "${value}". Expected the form "i/n", e.g. 1/3.`
    );
  }

  const index = Number.parseInt(match[1], 10);
  const total = Number.parseInt(match[2], 10);
  if (index <= 0 || total <= 0 || index > total) {
    throw new Error(`Invalid --shard value "${value}". Expected 1 <= i <= n.`);
  }

  return { index, total };
}

export function resolveRequestedFiles(fileNames, productDir, invocationCwd = process.cwd()) {
  const resolved = [];
  const seen = new Set();

  for (const rawFile of fileNames || []) {
    const normalized = resolveRequestedFile(rawFile, productDir, invocationCwd);
    if (seen.has(normalized)) continue;
    seen.add(normalized);
    resolved.push(normalized);
  }

  return resolved;
}

function resolveRequestedFile(rawFile, productDir, invocationCwd) {
  const rawValue = String(rawFile);
  const candidates = path.isAbsolute(rawValue)
    ? [rawValue]
    : [path.resolve(invocationCwd, rawValue), path.resolve(productDir, rawValue)];

  for (const candidate of candidates) {
    const relative = normalizePathSeparators(path.relative(productDir, candidate));
    if (!relative.startsWith("../") && relative !== ".." && !path.isAbsolute(relative)) {
      return relative.replace(/^\.\/+/, "");
    }
  }

  return normalizePathSeparators(rawValue).replace(/^\.\/+/, "");
}

function normalizePathSeparators(filePath) {
  return String(filePath).split(path.sep).join("/");
}
