import type { Token } from './core/types';
import { normalizeWhitespace, tokenizeEN } from './core/index';
import { segmentGraphemes } from './grapheme/index';
import { generateBreakpoints } from './breakpoints/index';
import { DEFAULT_LEXICONS, DEFAULT_WEIGHTS } from './scoring/index';
import { searchBestLayoutApp } from './searchApp/index';
import { searchBestLayoutWidget } from './searchWidget/index';
import { applyOverflowFallback } from './overflow/index';

import type { WrapTextInput, WrapTextMeta, WrapTextOutput } from './types';

function tokenizeTC(normalizedText: string): Token[] {
  const { clusters } = segmentGraphemes(normalizedText, 'PREFERRED');
  const tokens: Token[] = [];
  let idx = 0;
  for (const c of clusters) {
    const start = idx;
    idx += c.length;
    tokens.push({ text: c, start, end: idx });
  }
  return tokens;
}

export async function wrapText(input: WrapTextInput): Promise<WrapTextOutput> {
  const lang = input.lang;
  const context = input.context;
  const configVersion = input.configVersion ?? 'v1';
  const overflowMode: WrapTextInput['overflowMode'] =
    input.overflowMode ?? (context === 'WIDGET' ? 'ELLIPSIS' : 'CLIP');
  const lineMode = input.lineMode ?? 'AUTO';
  const debug = Boolean(input.debug);

  const { normalizedText } = normalizeWhitespace(input.text, 'NORMALIZE');
  const tokens = lang === 'EN' ? tokenizeEN(normalizedText) : tokenizeTC(normalizedText);

  // 断点候选
  const { breakpoints } = generateBreakpoints({
    tokens: tokens as any,
    lang,
    maxLines: input.maxLines,
    constraints: input.constraints,
    config: { tcMaxCandidateBreaks: 80, tcPunctuations: ['，', '。', '！', '？', '；', '：', '、'], balanceRange: 3 },
  });

  // scoring：protectedPhrases 从 constraints 注入
  const lexicons = { ...DEFAULT_LEXICONS, protectedPhrases: input.constraints?.protectedPhrases ?? DEFAULT_LEXICONS.protectedPhrases };
  const tcPunctuations: string[] = ['，', '。', '！', '？', '；', '：', '、'];
  const scoringConfig = {
    weights: DEFAULT_WEIGHTS,
    idealWidthRatio: { APP: 0.9, WIDGET: 0.95 },
    ellipsisToken: '…',
    tcParticleWhitelist: [],
    tcPunctuations,
  };

  // 搜索
  if (context === 'APP') {
    const res = await searchBestLayoutApp({
      tokens,
      lang,
      breakpoints: breakpoints as any,
      availableWidth: input.availableWidth,
      maxLines: input.maxLines,
      lineMode,
      measure: {
        contextProfile: input.contextProfile ?? 'APP',
        fontSpec: input.fontSpec ?? null,
        measureWidthImpl: input.measureWidthImpl,
      },
      scoring: { config: scoringConfig as any, lexicons: lexicons as any, debug },
    });

    if (res.ok) {
      const meta: WrapTextMeta = { configVersion, breaks: res.bestLayout.meta?.breaks, scoreTopTerms: res.bestLayout.meta?.scoreTopTerms };
      return { lines: res.bestLayout.lines, wrappedText: res.bestLayout.wrappedText, meta };
    }

    // APP：若测量失败/不可用，且调用方未要求 SYSTEM_DEFAULT，则尝试降级为“近似宽度 Beam”
    // 目的：保证尽量产出带 \n 的 wrappedText（而不是完全依赖系统自动换行）
    if (overflowMode !== 'SYSTEM_DEFAULT' && (res.reason === 'WIDTH_UNKNOWN' || res.reason === 'MEASURE_FAILED')) {
      const fontSize = Number.isFinite((input.fontSpec as any)?.fontSize) ? Number((input.fontSpec as any)?.fontSize) : 22;
      // 把像素宽度粗略换算为“token 容量”，只用于降级路径（确定性）
      const approxCapacity =
        lang === 'EN'
          ? Math.max(1, Math.floor(input.availableWidth / Math.max(1, Math.round(fontSize * 0.55))))
          : Math.max(1, Math.floor(input.availableWidth / Math.max(1, Math.round(fontSize * 0.95))));

      const approxRes = await searchBestLayoutWidget({
        tokens,
        lang,
        breakpoints: breakpoints as any,
        availableWidth: approxCapacity,
        maxLines: input.maxLines,
        context: 'WIDGET',
        measure: { widthMode: 'APPROX' },
        scoring: { config: scoringConfig as any, lexicons: lexicons as any, debug },
        config: { beamK: 5, expandM: 12 },
      });

      if (approxRes.ok) {
        const meta: WrapTextMeta = {
          configVersion,
          breaks: approxRes.bestLayout.meta?.breaks,
          scoreTopTerms: approxRes.bestLayout.meta?.scoreTopTerms,
          reason: res.reason,
        };
        return { lines: approxRes.bestLayout.lines, wrappedText: approxRes.bestLayout.wrappedText, meta };
      }
    }

    // overflow/fallback（最终兜底）
    const fb = await applyOverflowFallback({
      tokens,
      lang,
      context: 'APP',
      availableWidth: input.availableWidth,
      maxLines: input.maxLines,
      overflowMode: overflowMode as any,
      ellipsisToken: scoringConfig.ellipsisToken,
      reason: res.reason,
      partialLayout: null,
      tcPunctuations: scoringConfig.tcPunctuations,
    });

    return {
      lines: fb.lines,
      wrappedText: fb.wrappedText,
      meta: { configVersion, fallback_type: fb.meta.fallback_type, overflow_type: fb.meta.overflow_type, reason: fb.meta.reason },
    };
  }

  // WIDGET：默认 APPROX（单位由 availableWidth 决定），可在上层选择 MEASURE 并注入测量能力
  const resW = await searchBestLayoutWidget({
    tokens,
    lang,
    breakpoints: breakpoints as any,
    availableWidth: input.availableWidth,
    maxLines: input.maxLines,
    context: 'WIDGET',
    measure: { widthMode: 'APPROX' },
    scoring: { config: scoringConfig as any, lexicons: lexicons as any, debug },
  });

  if (resW.ok) {
    const meta: WrapTextMeta = {
      configVersion,
      breaks: resW.bestLayout.meta?.breaks,
      scoreTopTerms: resW.bestLayout.meta?.scoreTopTerms,
      reason: resW.bestLayout.meta?.reason,
    };
    return { lines: resW.bestLayout.lines, wrappedText: resW.bestLayout.wrappedText, meta };
  }

  const fb = await applyOverflowFallback({
    tokens,
    lang,
    context: 'WIDGET',
    availableWidth: input.availableWidth,
    maxLines: input.maxLines,
    overflowMode: overflowMode as any,
    ellipsisToken: scoringConfig.ellipsisToken,
    reason: resW.reason,
    partialLayout: null,
    tcPunctuations: scoringConfig.tcPunctuations,
  });

  return {
    lines: fb.lines,
    wrappedText: fb.wrappedText,
    meta: { configVersion, fallback_type: fb.meta.fallback_type, overflow_type: fb.meta.overflow_type, reason: fb.meta.reason },
  };
}

