import type { Breakpoint, BreakpointKind, BreakpointMeta, BreakpointConstraints } from './types';

const KIND_ORDER: Record<BreakpointKind, number> = {
  PUNCT: 0,
  SPACE: 1,
  BALANCE: 2,
  OTHER: 3,
};

function betterBreakpoint(a: Breakpoint, b: Breakpoint): Breakpoint {
  if (a.priority !== b.priority) return a.priority > b.priority ? a : b;
  const ka = KIND_ORDER[a.kind] ?? 999;
  const kb = KIND_ORDER[b.kind] ?? 999;
  if (ka !== kb) return ka < kb ? a : b;
  // 完全相同优先级时，稳定选择 pos 更小者（但同 pos 才会进入该比较）
  return a;
}

/**
 * forbiddenBreakRanges 过滤（闭区间口径）
 * - start <= pos && pos <= end 命中则剔除
 */
export function filterForbiddenBreakRanges(
  breakpoints: Breakpoint[],
  constraints: BreakpointConstraints | undefined
): Breakpoint[] {
  const ranges = constraints?.forbiddenBreakRanges;
  if (!ranges || ranges.length === 0) return breakpoints;

  return breakpoints.filter((bp) => {
    for (const r of ranges) {
      const s = r.start | 0;
      const e = r.end | 0;
      if (s <= bp.pos && bp.pos <= e) return false;
    }
    return true;
  });
}

/**
 * 去重：同 pos 只保留一个 breakpoint（priority 更高者优先；同 priority 按 kind 固定序）
 */
export function dedupByPos(breakpoints: Breakpoint[]): Breakpoint[] {
  const map = new Map<number, Breakpoint>();
  for (const bp of breakpoints) {
    const prev = map.get(bp.pos);
    if (!prev) {
      map.set(bp.pos, bp);
      continue;
    }
    map.set(bp.pos, betterBreakpoint(prev, bp));
  }
  return Array.from(map.values());
}

export function distanceToNearestIdeal(pos: number, ideals: number[] | undefined): number {
  if (!ideals || ideals.length === 0) return 1_000_000_000;
  let best = 1_000_000_000;
  for (const p of ideals) {
    const d = Math.abs(pos - p);
    if (d < best) best = d;
  }
  return best;
}

/**
 * TC 裁剪（确定性排序后截断）
 * - priority desc
 * - distToIdeal asc
 * - pos asc
 */
export function pruneTcCandidates(breakpoints: Breakpoint[], ideals: number[], tcMaxCandidateBreaks: number): Breakpoint[] {
  if (breakpoints.length <= tcMaxCandidateBreaks) return breakpoints;

  const sorted = [...breakpoints].sort((a, b) => {
    if (a.priority !== b.priority) return b.priority - a.priority;
    const da = distanceToNearestIdeal(a.pos, ideals);
    const db = distanceToNearestIdeal(b.pos, ideals);
    if (da !== db) return da - db;
    return a.pos - b.pos;
  });

  return sorted.slice(0, tcMaxCandidateBreaks);
}

export function buildMeta(originalCount: number, finalCount: number): BreakpointMeta {
  return { originalCount, finalCount, pruned: finalCount < originalCount };
}

