import path from "path";

export function parsePlaywrightJsonResults(stdout, cwd) {
  if (!stdout.trim()) {
    return { fileResults: new Map(), errors: [] };
  }

  let parsed;
  try {
    parsed = JSON.parse(stdout);
  } catch (error) {
    return {
      fileResults: new Map(),
      errors: [`Could not parse Playwright JSON output: ${formatError(error)}`],
    };
  }

  const fileResults = new Map();
  visitPlaywrightSuites(parsed.suites || [], null, fileResults, cwd);
  return {
    fileResults,
    errors: (parsed.errors || []).map(formatPlaywrightReporterError).filter(Boolean),
  };
}

export function visitPlaywrightSuites(suites, inheritedFile, fileResults, cwd) {
  for (const suite of suites || []) {
    const suiteFile = normalizeReportedFile(extractReporterFile(suite) || inheritedFile, cwd);
    for (const child of suite.suites || []) {
      visitPlaywrightSuites([child], suiteFile, fileResults, cwd);
    }
    for (const spec of suite.specs || []) {
      collectPlaywrightSpec(spec, suiteFile, fileResults, cwd);
    }
  }
}

export function collectPlaywrightSpec(spec, inheritedFile, fileResults, cwd) {
  const file = normalizeReportedFile(extractReporterFile(spec) || inheritedFile, cwd);
  if (!file) return;

  const current = fileResults.get(file) || {
    failed: false,
    error: null,
    durationMs: 0,
  };

  for (const test of spec.tests || []) {
    const results = Array.isArray(test.results) ? test.results : [];
    current.durationMs += results.reduce(
      (sum, result) => sum + Number(result?.duration || 0),
      0
    );

    const final = choosePlaywrightFinalResult(results);
    const failed =
      test.outcome === "unexpected" ||
      !isPlaywrightPassingStatus(final?.status);

    if (failed) {
      current.failed = true;
      current.error ||= extractPlaywrightFailure(final, spec, test);
    }
  }

  fileResults.set(file, current);
}

export function choosePlaywrightFinalResult(results) {
  if (!results || results.length === 0) return null;
  return results[results.length - 1];
}

export function isPlaywrightPassingStatus(status) {
  return !status || ["passed", "skipped", "expected"].includes(status);
}

export function extractPlaywrightFailure(finalResult, spec, test) {
  const fromResult =
    finalResult?.error?.message ||
    finalResult?.error?.value ||
    finalResult?.error?.stack;
  if (fromResult) return firstLine(fromResult);

  const fromTest = test?.errors?.[0]?.message;
  if (fromTest) return firstLine(fromTest);

  return firstLine(spec?.title || "Playwright test failed");
}

export function formatPlaywrightReporterError(error) {
  if (!error) return null;
  if (typeof error === "string") return firstLine(error);
  if (typeof error.message === "string") return firstLine(error.message);
  if (typeof error.value === "string") return firstLine(error.value);
  return null;
}

export function extractReporterFile(node) {
  if (!node || typeof node !== "object") return null;
  if (typeof node.file === "string" && node.file.length > 0) return node.file;
  if (node.location && typeof node.location.file === "string" && node.location.file.length > 0) {
    return node.location.file;
  }
  return null;
}

export function normalizeReportedFile(filePath, cwd) {
  if (!filePath) return null;
  const absolute = path.isAbsolute(filePath) ? filePath : path.join(cwd, filePath);
  return normalizePathSeparators(path.relative(cwd, absolute));
}

export function firstLine(value) {
  return String(value).split(/\r?\n/).find((line) => line.trim().length > 0)?.trim() || null;
}

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

function formatError(error) {
  if (error instanceof Error) return error.message;
  return String(error);
}
