import type { ContextProfile, FontSpec, MeasureResult, MeasureWidthImpl, TextWrapContext } from './types';
import { LruCache } from './cache';
import { assertFontSpecComplete, buildFontSpecKey } from './fontSpecKey';

// 模块级常驻缓存（两级）
const textWidthCache = new LruCache<Promise<number>>(2000);
const loggedMeasureFailures = new Set<string>();

function buildTextKey(args: { contextProfile: ContextProfile; fontSpecKey: string; text: string }): string {
  // key 拼接规则必须确定性：<contextProfile>|<fontSpecKey>|<text>
  return `${args.contextProfile}|${args.fontSpecKey}|${args.text}`;
}

function isValidWidth(v: unknown): v is number {
  return typeof v === 'number' && Number.isFinite(v) && v >= 0;
}

/**
 * 测量文本宽度（带缓存）。
 *
 * 约束：
 * - 若启用测量（提供 measureWidthImpl），fontSpec 必须完整；缺字段直接报错（简体中文）
 * - 若不启用测量（measureWidthImpl 缺失或 context=WIDGET 且明确不启用），进入 approx mode：width=null
 */
export async function measureWidthCached(args: {
  text: string;
  context: TextWrapContext;
  contextProfile: ContextProfile;
  fontSpec?: Partial<FontSpec> | null;
  measureWidthImpl?: MeasureWidthImpl;
  /** WIDGET 场景是否启用测量；默认 false（未启用即 WIDTH_UNKNOWN） */
  widgetEnableMeasure?: boolean;
}): Promise<MeasureResult> {
  const text = String(args.text ?? '');

  const enabled =
    typeof args.measureWidthImpl === 'function' && (args.context === 'APP' || args.widgetEnableMeasure === true);

  if (!enabled) {
    return { width: null, meta: { isApprox: true, reason: 'WIDTH_UNKNOWN' } };
  }

  // 启用测量时：fontSpec 缺字段必须报错
  const fontSpec = args.fontSpec;
  assertFontSpecComplete(fontSpec);
  const fontSpecKey = buildFontSpecKey(fontSpec);
  const key = buildTextKey({ contextProfile: args.contextProfile, fontSpecKey, text });

  const cached = textWidthCache.get(key);
  if (cached) {
    try {
      const w = await cached;
      return isValidWidth(w) ? { width: w, meta: { isApprox: false } } : { width: null, meta: { isApprox: true, reason: 'MEASURE_FAILED' } };
    } catch {
      if (__DEV__ && !loggedMeasureFailures.has(key)) {
        loggedMeasureFailures.add(key);
        console.log('[TextWrap][Measure] measureWidthCached: 命中缓存但 Promise 失败（MEASURE_FAILED）', {
          context: args.context,
          contextProfile: args.contextProfile,
          fontSpec: args.fontSpec,
          textPreview: text.slice(0, 80),
          textLength: text.length,
        });
      }
      return { width: null, meta: { isApprox: true, reason: 'MEASURE_FAILED' } };
    }
  }

  const promise = (async () => {
    try {
      const w = await args.measureWidthImpl!({
        text,
        context: args.context,
        contextProfile: args.contextProfile,
        fontSpec,
      });
      if (!isValidWidth(w)) {
        throw new Error('测量结果非法（必须为有限且非负的 number）。');
      }
      return w;
    } catch (error) {
      if (__DEV__ && !loggedMeasureFailures.has(key)) {
        loggedMeasureFailures.add(key);
        console.log('[TextWrap][Measure] measureWidthImpl 抛错（MEASURE_FAILED）', {
          context: args.context,
          contextProfile: args.contextProfile,
          fontSpec,
          textPreview: text.slice(0, 80),
          textLength: text.length,
          errorName: (error as any)?.name,
          errorMessage: String((error as any)?.message ?? error),
          errorStack: (error as any)?.stack,
        });
      }
      throw error;
    }
  })();

  textWidthCache.set(key, promise);

  try {
    const w = await promise;
    return { width: w, meta: { isApprox: false } };
  } catch {
    // 若失败，删除缓存，避免缓存住失败结果
    textWidthCache.delete(key);
    return { width: null, meta: { isApprox: true, reason: 'MEASURE_FAILED' } };
  }
}

