import { canonicalize } from "../../../schema/src/canonical.js";
import type { CoverageOutcome } from "../../../schema/src/records/coverage.js";
import type { Finding } from "../../../schema/src/records/finding.js";
import type { HarnessOutcome } from "../../../schema/src/records/harness.js";

const LANE_ORDER: Record<Finding["lane"], number> = {
  blocking: 0,
  advisory: 1,
  accepted: 2,
  resolved: 3,
};

const SEVERITY_ORDER: Record<Finding["severity"], number> = {
  critical: 0,
  high: 1,
  medium: 2,
  low: 3,
  info: 4,
};

function compareUnicodeScalars(left: string, right: string): number {
  let leftIndex = 0;
  let rightIndex = 0;

  while (leftIndex < left.length && rightIndex < right.length) {
    const leftCode = left.codePointAt(leftIndex);
    const rightCode = right.codePointAt(rightIndex);
    if (leftCode === undefined || rightCode === undefined) {
      break;
    }
    if (leftCode !== rightCode) {
      return leftCode < rightCode ? -1 : 1;
    }
    leftIndex += leftCode > 0xffff ? 2 : 1;
    rightIndex += rightCode > 0xffff ? 2 : 1;
  }

  return left.length - right.length;
}

function compareFindings(left: Finding, right: Finding): number {
  const laneDelta = LANE_ORDER[left.lane] - LANE_ORDER[right.lane];
  if (laneDelta !== 0) {
    return laneDelta;
  }

  const severityDelta = SEVERITY_ORDER[left.severity] - SEVERITY_ORDER[right.severity];
  if (severityDelta !== 0) {
    return severityDelta;
  }

  const classDelta = compareUnicodeScalars(left.class, right.class);
  if (classDelta !== 0) {
    return classDelta;
  }

  const targetDelta = compareUnicodeScalars(left.target.canonical, right.target.canonical);
  if (targetDelta !== 0) {
    return targetDelta;
  }

  const contextDelta = compareUnicodeScalars(
    canonicalize(left.context),
    canonicalize(right.context),
  );
  if (contextDelta !== 0) {
    return contextDelta;
  }

  return compareUnicodeScalars(left.id, right.id);
}

export function sortFindings(findings: Finding[]): Finding[] {
  return [...findings].sort(compareFindings);
}

export function sortById<T extends { id: string }>(records: T[]): T[] {
  return [...records].sort((left, right) => compareUnicodeScalars(left.id, right.id));
}

export function sortCoverageOutcomes(outcomes: CoverageOutcome[]): CoverageOutcome[] {
  return sortById(outcomes);
}

export function sortHarnessOutcomes(outcomes: HarnessOutcome[]): HarnessOutcome[] {
  return sortById(outcomes);
}
