const AUTO_THRESHOLD_RATIO = 0.3;
const AUTO_THRESHOLD_MIN = 2;

export type IdentifierResolution = { kind: 'auto'; value: string } | { kind: 'suggest'; value: string };

export function chooseClosestIdentifier(input: string, candidates: string[]): IdentifierResolution | undefined {
  if (candidates.length === 0) {
    return undefined;
  }
  const normalizedInput = normalizeIdentifier(input);
  let bestName: string | undefined;
  let bestScore = Number.POSITIVE_INFINITY;

  for (const candidate of candidates) {
    if (candidate === input) {
      return { kind: 'auto', value: candidate };
    }
    const normalizedCandidate = normalizeIdentifier(candidate);
    if (normalizedCandidate === normalizedInput) {
      return { kind: 'auto', value: candidate };
    }
    if (candidate.toLowerCase() === input.toLowerCase()) {
      return { kind: 'auto', value: candidate };
    }
    const score = levenshtein(normalizedInput, normalizedCandidate);
    if (score < bestScore) {
      bestScore = score;
      bestName = candidate;
    }
  }

  if (!bestName) {
    return undefined;
  }

  const normalizedBest = normalizeIdentifier(bestName);
  const lengthBaseline = Math.max(normalizedInput.length, normalizedBest.length, 1);
  const threshold = Math.max(AUTO_THRESHOLD_MIN, Math.floor(lengthBaseline * AUTO_THRESHOLD_RATIO));
  if (bestScore <= threshold) {
    return { kind: 'auto', value: bestName };
  }
  return { kind: 'suggest', value: bestName };
}

export function normalizeIdentifier(value: string): string {
  return value.replace(/[^a-z0-9]/gi, '').toLowerCase();
}

export interface IdentifierResolutionContext {
  entity: 'server' | 'tool';
  attempted: string;
  resolution: IdentifierResolution;
  scope?: string;
}

export function renderIdentifierResolutionMessages(context: IdentifierResolutionContext): {
  auto?: string;
  suggest?: string;
} {
  const resolvedDisplay =
    context.entity === 'tool' && context.scope
      ? `${context.scope}.${context.resolution.value}`
      : context.resolution.value;
  const attemptedDisplay =
    context.entity === 'tool' && context.scope ? `${context.scope}.${context.attempted}` : context.attempted;
  if (context.resolution.kind === 'auto') {
    const noun = context.entity === 'tool' ? 'tool call' : 'server name';
    return {
      auto: `[mcporter] Auto-corrected ${noun} to ${resolvedDisplay} (input: ${attemptedDisplay}).`,
    };
  }
  return {
    suggest: `[mcporter] Did you mean ${resolvedDisplay}?`,
  };
}

function levenshtein(a: string, b: string): number {
  if (a === b) {
    return 0;
  }
  if (a.length === 0) {
    return b.length;
  }
  if (b.length === 0) {
    return a.length;
  }

  const previous: number[] = Array.from({ length: b.length + 1 }, (_, index) => index);
  const current: number[] = Array.from({ length: b.length + 1 }, () => 0);

  for (let i = 1; i <= a.length; i += 1) {
    current[0] = i;
    const charA = a[i - 1];
    for (let j = 1; j <= b.length; j += 1) {
      const charB = b[j - 1];
      const insertCost = (current[j - 1] ?? Number.POSITIVE_INFINITY) + 1;
      const deleteCost = (previous[j] ?? Number.POSITIVE_INFINITY) + 1;
      const replaceCost = (previous[j - 1] ?? Number.POSITIVE_INFINITY) + (charA === charB ? 0 : 1);
      current[j] = Math.min(insertCost, deleteCost, replaceCost);
    }
    for (let j = 0; j <= b.length; j += 1) {
      previous[j] = current[j] ?? Number.POSITIVE_INFINITY;
    }
  }

  return previous[b.length] ?? Number.POSITIVE_INFINITY;
}
