import fs from "fs";
import path from "path";
import { resolveProductDir } from "../config/index.mjs";
import { loadTestkitSetup } from "../config/setup-loader.mjs";
import {
  buildKnownFailureIssueValidationSummaryLines,
  normalizeKnownFailureIssueValidationConfig,
  shouldFailKnownFailureIssueValidation,
  validateKnownFailureIssues,
} from "../known-failures/github.mjs";
import {
  loadKnownFailuresDocument,
  renderKnownFailuresMarkdown,
  validateKnownFailuresDocument,
} from "../known-failures/index.mjs";
import { collectGitMetadata } from "../runner/metadata.mjs";

const DEFAULT_ISSUE_VALIDATION = {
  provider: "github",
  mode: "error",
  cacheTtlSeconds: 900,
};

export async function runKnownFailuresRenderCommand(options = {}) {
  const context = await resolveKnownFailuresContext(options);
  const document = loadKnownFailuresDocument(context.knownFailuresPath, context.knownFailuresRelativePath);
  const markdown = renderKnownFailuresMarkdown(document);

  if (options.output) {
    const outputPath = path.resolve(context.productDir, options.output);
    fs.mkdirSync(path.dirname(outputPath), { recursive: true });
    fs.writeFileSync(outputPath, markdown);
    console.log(`Wrote ${path.relative(context.productDir, outputPath)}`);
    return;
  }

  process.stdout.write(markdown);
}

export async function runKnownFailuresValidateCommand(options = {}) {
  const context = await resolveKnownFailuresContext(options);
  const document = loadKnownFailuresDocument(context.knownFailuresPath, context.knownFailuresRelativePath);
  const statusArtifact = context.statusArtifactPath
    ? readJsonIfExists(context.statusArtifactPath)
    : undefined;

  const documentValidation = validateKnownFailuresDocument(document, {
    productDir: context.productDir,
    statusArtifact,
  });

  const issueValidation = await validateKnownFailureIssues({
    productDir: context.productDir,
    document,
    statusArtifact,
    config: context.issueValidationConfig,
    gitMetadata: collectGitMetadata(context.productDir),
  });

  const issueErrors = collectIssueMessages(issueValidation, "error");
  const issueWarnings = collectIssueMessages(issueValidation, "warning");
  const hasErrors =
    documentValidation.errors.length > 0 ||
    shouldFailKnownFailureIssueValidation(issueValidation) ||
    issueErrors.length > 0;

  if (hasErrors) {
    console.error("Known failures validation failed:");
    for (const error of documentValidation.errors) {
      console.error(`- ${error}`);
    }
    for (const error of issueErrors) {
      console.error(`- ${error}`);
    }
    if (documentValidation.warnings.length > 0 || issueWarnings.length > 0) {
      console.error("");
      for (const warning of documentValidation.warnings) {
        console.error(`! ${warning}`);
      }
      for (const warning of issueWarnings) {
        console.error(`! ${warning}`);
      }
    }
    for (const line of buildKnownFailureIssueValidationSummaryLines(issueValidation)) {
      console.error(line);
    }
    process.exitCode = 1;
    return;
  }

  console.log(
    `Known failures valid: ${documentValidation.stats.entries} entries, ${documentValidation.stats.matches} matches`
  );
  if (documentValidation.stats.failedTests > 0) {
    console.log(
      `Status coverage: ${documentValidation.stats.triagedFailedTests}/${documentValidation.stats.failedTests} failed tests triaged`
    );
  }
  for (const line of buildKnownFailureIssueValidationSummaryLines(issueValidation)) {
    console.log(line);
  }
  for (const warning of [...documentValidation.warnings, ...issueWarnings]) {
    console.warn(`! ${warning}`);
  }
}

async function resolveKnownFailuresContext(options) {
  const productDir = resolveProductDir(process.cwd(), options.dir);
  const { setup } = await loadTestkitSetup(productDir);
  const reporting = setup?.reporting || {};

  const knownFailuresRelativePath = normalizeOptionalString(options.input)
    || normalizeOptionalString(reporting.knownFailuresFile);
  if (!knownFailuresRelativePath) {
    throw new Error(
      "Known failures file not configured. Set reporting.knownFailuresFile in testkit.setup.ts or pass --input."
    );
  }

  const statusRelativePath = normalizeOptionalString(options.status) || "testkit.status.json";
  const configuredIssueValidation = normalizeKnownFailureIssueValidationConfig(reporting.issueValidation)
    || DEFAULT_ISSUE_VALIDATION;
  const issueMode = normalizeOptionalString(options.issueMode) || "error";

  return {
    productDir,
    knownFailuresRelativePath,
    knownFailuresPath: path.resolve(productDir, knownFailuresRelativePath),
    statusArtifactPath: statusRelativePath ? path.resolve(productDir, statusRelativePath) : null,
    issueValidationConfig: {
      ...configuredIssueValidation,
      mode: issueMode,
    },
  };
}

function collectIssueMessages(validation, severity) {
  if (!validation) return [];

  const messages = [
    ...validation.findings
      .filter((finding) => finding.severity === severity)
      .map((finding) => finding.message),
  ];
  for (const entry of validation.entries) {
    for (const finding of entry.findings) {
      if (finding.severity === severity) {
        messages.push(finding.message);
      }
    }
  }
  return [...new Set(messages)];
}

function normalizeOptionalString(value) {
  if (typeof value !== "string") return null;
  const normalized = value.trim();
  return normalized.length > 0 ? normalized : null;
}

function readJsonIfExists(filePath) {
  if (!fs.existsSync(filePath)) return undefined;
  return JSON.parse(fs.readFileSync(filePath, "utf8"));
}
