import { buildDeck, isRed, pairName, shuffle, singleValue } from './cards';
import {
  bestSplit,
  compareSplit,
  evalGroup,
  specialHand,
  splitFromChosen,
  type SplitHand,
} from './handEval';
import type {
  BetAction,
  Card,
  GameState,
  Jackpot,
  JackpotHit,
  PlayerResult,
  Seat,
} from './types';

const NAMES = ['我', '老王', '幺妹', '张三', '李四', '赵六'];
const AVATARS = ['🧑', '👨', '👩', '🧓', '🧔', '👴'];
const START_CHIPS = 2000;
const BASE_CHIP = 20; // 底皮（每局底注）
const BOBO = 200; // 簸簸（本局最多投入）

function makeJackpot(): GameState['jackpot'] {
  const recs = Array.from({ length: 6 }).map(() => ({
    player: '六字昵称',
    hand: '天牌',
    amount: 268,
    date: '2024/4/28',
  }));
  return {
    total: 148624,
    levels: [
      { label: '1皮', amount: 14442 },
      { label: '2皮', amount: 14442 },
      { label: '5皮', amount: 14442 },
      { label: '10皮', amount: 14442 },
      { label: '20皮', amount: 14442 },
      { label: '50皮', amount: 14442 },
    ],
    rewards: [
      { hand: '天牌', percent: 20 },
      { hand: '地牌', percent: 10 },
      { hand: '人牌', percent: 2 },
    ],
    topWinner: { player: '追风浪子', hand: '天牌', amount: 888, date: '2024/4/28' },
    records: recs,
    note: '官方规则：拿到天牌触发神秘奖励，地牌天下无敌。奖池随各局累积。',
  };
}

function blankSeat(i: number): Seat {
  return {
    id: i === 0 ? 'hero' : `p${i + 1}`,
    name: NAMES[i],
    avatar: AVATARS[i],
    chips: START_CHIPS,
    betThisRound: 0,
    totalBet: 0,
    status: 'playing',
    isDealer: false,
    isHero: i === 0,
    isActive: false,
    cards: [],
  };
}

/** 初始化整桌（用于首次进入） */
export function createGame(baseChip = BASE_CHIP): GameState {
  const seats = Array.from({ length: 6 }).map((_, i) => blankSeat(i));
  const base: GameState = {
    seats,
    pot: 0,
    bobo: BOBO,
    baseChip,
    round: 1,
    currentBet: 0,
    mangoPool: 0,
    mangoMult: 1,
    piPool: 0,
    roomId: '418417',
    dealSeq: 0,
    rule: '手手芒 / 休芒 / 皱芒',
    phase: 'dealing',
    actorIndex: -1,
    dealerIndex: 0,
    jackpot: makeJackpot(),
  };
  return startHand(base, 0);
}

/** 开新一局：洗牌、发 2 张暗牌、收底皮、进入第 1 轮下注 */
export function startHand(prev: GameState, dealerIndex: number): GameState {
  const deck = shuffle(buildDeck(false));
  let di = 0;
  const seats: Seat[] = prev.seats.map((s, i) => {
    const cards = [deck[di++], deck[di++]].map((c) => ({ ...c, faceUp: false }));
    return {
      ...s,
      cards,
      betThisRound: 0,
      totalBet: prev.baseChip,
      chips: s.chips - prev.baseChip,
      status: 'playing',
      allIn: false,
      actedThisRound: false,
      isDealer: i === dealerIndex,
      isActive: false,
      tag: undefined,
      tagKind: undefined,
      handHint: undefined,
      cardHint: undefined,
    } as Seat;
  });
  // 暂存未发的明牌在闭包外不便，直接把整副存进 module 级别？改为放进 state 不优雅。
  // 这里把剩余牌挂到第一个 seat 的隐藏字段不合适——改用 deckRef 模块变量。
  deckRef = deck;
  deckPos = di;

  const state: GameState = {
    ...prev,
    seats,
    pot: prev.baseChip * seats.length,
    round: 1,
    currentBet: 0,
    mangoMult: 1,
    dealSeq: prev.dealSeq + 1,
    phase: 'betting',
    dealerIndex,
    actorIndex: dealerIndex,
    message: '第 1 轮 · 发 2 张底牌',
    results: undefined,
    jackpotHit: undefined,
  };
  return setActive(state, dealerIndex);
}

// 模块级牌堆（当前局），随 startHand 重置
let deckRef: Card[] = [];
let deckPos = 0;

/** 当前局剩余未发的牌（看剩余牌功能用） */
export function remainingDeck(): Card[] {
  return deckRef.slice(deckPos).map((c) => ({ ...c, faceUp: true }));
}

function setActive(state: GameState, idx: number): GameState {
  const seats = state.seats.map((s, i) => ({ ...s, isActive: i === idx }));
  return { ...state, seats, actorIndex: idx };
}

function activeContenders(seats: Seat[]): number[] {
  return seats
    .map((s, i) => (s.status !== 'folded' ? i : -1))
    .filter((i) => i >= 0);
}

/** 仍需发话的玩家（未丢、未全下、未跟齐或本轮未发话） */
function needsAction(s: Seat, currentBet: number): boolean {
  if (s.status === 'folded' || s.allIn) return false;
  return s.betThisRound < currentBet || !s.actedThisRound;
}

function nextActor(seats: Seat[], from: number, currentBet: number): number {
  const n = seats.length;
  for (let step = 1; step <= n; step++) {
    const idx = (from + step) % n;
    if (needsAction(seats[idx], currentBet)) return idx;
  }
  return -1;
}

const TAG_BY_ACTION: Record<BetAction, { text: string; kind: NonNullable<Seat['tagKind']> }> = {
  raise: { text: '大', kind: 'raise' },
  call: { text: '跟', kind: 'call' },
  knock: { text: '敲', kind: 'knock' },
  rest: { text: '休', kind: 'rest' },
  fold: { text: '丢!', kind: 'fold' },
};

/**
 * 执行一个下注动作。raiseAmount 仅在 action='raise' 时使用（加注的增量）。
 */
export function applyAction(
  state: GameState,
  action: BetAction,
  raiseAmount = state.baseChip,
): GameState {
  if (state.phase !== 'betting') return state;
  const idx = state.actorIndex;
  if (idx < 0) return state;
  const seats = state.seats.map((s) => ({ ...s }));
  const seat = seats[idx];
  let { pot, currentBet } = state;
  let message = '';

  if (action === 'fold') {
    seat.status = 'folded';
    message = `${seat.name} 丢`;
  } else if (action === 'rest') {
    // 仅当本轮无人下注时有效
    message = `${seat.name} 休`;
  } else if (action === 'call') {
    const pay = Math.min(currentBet - seat.betThisRound, seat.chips);
    seat.chips -= pay;
    seat.betThisRound += pay;
    seat.totalBet += pay;
    pot += pay;
    if (seat.totalBet >= state.bobo || seat.chips === 0) seat.allIn = true;
    message = `${seat.name} 跟 ${currentBet}`;
  } else if (action === 'raise') {
    const committedBefore = seat.totalBet - seat.betThisRound;
    const maxBetThisRound = Math.min(
      state.bobo - committedBefore,
      seat.betThisRound + seat.chips,
    );
    const target = Math.min(currentBet + Math.max(raiseAmount, state.baseChip), maxBetThisRound);
    const pay = Math.max(target - seat.betThisRound, 0);
    seat.chips -= pay;
    seat.betThisRound += pay;
    seat.totalBet += pay;
    pot += pay;
    if (seat.totalBet >= state.bobo || seat.chips === 0) seat.allIn = true;
    if (seat.betThisRound > currentBet) {
      // 真正加注：抬高注额，其余玩家需重新应对
      currentBet = seat.betThisRound;
      message = `${seat.name} 大 → ${currentBet}`;
      seats.forEach((s, i) => {
        if (i !== idx && s.status !== 'folded' && !s.allIn) s.actedThisRound = false;
      });
    } else {
      // 已到上限无法再加，视同跟注
      message = `${seat.name} 跟 ${currentBet}`;
    }
  } else if (action === 'knock') {
    const pay = Math.min(state.bobo - seat.totalBet, seat.chips);
    seat.chips -= pay;
    seat.betThisRound += pay;
    seat.totalBet += pay;
    pot += pay;
    seat.allIn = true;
    seat.status = 'knocked';
    currentBet = Math.max(currentBet, seat.betThisRound);
    message = `${seat.name} 敲 (${seat.totalBet})`;
    seats.forEach((s, i) => {
      if (i !== idx && s.status !== 'folded' && !s.allIn) s.actedThisRound = false;
    });
  }

  seat.actedThisRound = true;
  const tag = TAG_BY_ACTION[action];
  seat.tag = tag.text;
  seat.tagKind = tag.kind;

  const next: GameState = { ...state, seats, pot, currentBet, message };

  // 只剩一名未丢牌玩家 → 本局立即结束（第 1 轮即为皱芒）
  const alive = activeContenders(seats);
  if (alive.length === 1) {
    return settle(next, true);
  }

  // 本轮是否结束
  const stillToAct = nextActor(seats, idx, currentBet);
  if (stillToAct === -1) {
    return endBettingRound(next);
  }
  return setActive(next, stillToAct);
}

/** 本轮下注结束：发下一轮明牌或进入分牌 */
function endBettingRound(state: GameState): GameState {
  if (state.round >= 3) {
    return enterSplit(state);
  }
  const seats = state.seats.map((s) => ({ ...s }));
  // 休芒：本轮无人下注（全员休）→ 每位未丢牌玩家各出 1 底皮入芒果池
  let mangoPool = state.mangoPool;
  let mangoMult = state.mangoMult;
  let mangoMsg = '';
  if (state.currentBet === 0) {
    seats.forEach((s) => {
      if (s.status !== 'folded' && s.chips >= state.baseChip) {
        s.chips -= state.baseChip;
        mangoPool += state.baseChip;
      }
    });
    mangoMult += 1; // 全员休一次 → 休芒倍数 +1
    mangoMsg = '（休芒：各出底皮入芒果池）';
  }
  // 给未丢牌玩家各发 1 张明牌
  seats.forEach((s) => {
    if (s.status !== 'folded') {
      const card = { ...deckRef[deckPos++], faceUp: true };
      s.cards = [...s.cards, card];
    }
    s.betThisRound = 0;
    s.actedThisRound = false;
    if (s.status !== 'folded' && !s.allIn && s.status !== 'knocked') s.tag = undefined;
  });
  const round = state.round + 1;
  // 新一轮由所发明牌最大者先说话
  const first = highestNewCardSeat(seats);
  let next: GameState = {
    ...state,
    seats,
    round,
    currentBet: 0,
    mangoPool,
    mangoMult,
    dealSeq: state.dealSeq + 1,
    message: `第 ${round} 轮 · 发明牌${mangoMsg}`,
  };
  next = refreshCardHints(next);
  return setActive(next, first);
}

function highestNewCardSeat(seats: Seat[]): number {
  let best = -1;
  let bestVal = -1;
  seats.forEach((s, i) => {
    if (s.status === 'folded') return;
    const last = s.cards[s.cards.length - 1];
    const v = singleValue(last);
    if (v > bestVal) {
      bestVal = v;
      best = i;
    }
  });
  return best >= 0 ? best : 0;
}

/**
 * 进入分牌阶段。对手自动分墩；本人若在场且非特殊牌型，则停在 split 阶段
 * 等待手动分牌（点两张作一墩）。本人已丢/特殊牌型则直接结算。
 */
function enterSplit(state: GameState): GameState {
  const heroIdx = state.seats.findIndex((s) => s.isHero);
  const hero = state.seats[heroIdx];
  const heroNeedsSplit =
    hero && hero.status !== 'folded' && !specialHand(hero.cards) && hero.cards.length === 4;

  if (!heroNeedsSplit) {
    const seats = state.seats.map((s) => ({ ...s, isActive: false }));
    return settle({ ...state, seats, phase: 'split', message: '分牌比大小' }, false);
  }

  const seats = state.seats.map((s) => {
    if (s.status === 'folded') return { ...s, isActive: false };
    if (s.isHero) return { ...s, isActive: true, tag: '分牌中', tagKind: 'splitting' as const };
    return { ...s, isActive: false, tag: '已分牌', tagKind: 'split' as const };
  });
  return {
    ...state,
    seats,
    phase: 'split',
    actorIndex: heroIdx,
    message: '请分牌：点两张牌作为一墩',
  };
}

/** 本人手动分牌后结算：headIds 为本人选定的一墩两张牌 id，mult 为本人下注倍数（×2/×4/×6） */
export function applyHeroSplit(state: GameState, headIds: string[], mult = 1): GameState {
  if (state.phase !== 'split') return state;
  const seats = state.seats.map((s) =>
    s.isHero ? { ...s, manualHeadIds: headIds, tag: '已分牌', tagKind: 'split' as const } : s,
  );
  const settled = settle({ ...state, seats }, false);
  return applyHeroMultiplier(settled, mult);
}

/**
 * 按本人选择的倍数放大本人本局输赢；对手按 |delta| 比例承担反向差额，保持筹码守恒。
 * 本人赢 D，倍数 m → 本人 +D·(m-1)，反向方（输家）按比例多付该差额；本人输则反之。
 */
function applyHeroMultiplier(state: GameState, mult: number): GameState {
  if (mult <= 1 || !state.results) return state;
  const heroSeat = state.seats.find((s) => s.isHero);
  if (!heroSeat) return state;
  const heroRes = state.results.find((r) => r.seatId === heroSeat.id);
  if (!heroRes || heroRes.delta === 0) return state;

  const D = heroRes.delta;
  const extra = D * (mult - 1);
  const sign = D > 0 ? 1 : -1;
  // 与本人盈亏相反的对手（赢家放大时由输家承担，反之亦然）
  const counter = state.results.filter(
    (r) => r.seatId !== heroSeat.id && Math.sign(r.delta) === -sign && r.delta !== 0,
  );
  const totalMag = counter.reduce((sum, r) => sum + Math.abs(r.delta), 0);
  if (counter.length === 0 || totalMag === 0) return state;

  // 按比例分摊 -extra（取整，余数计入承担最多者，确保守恒）
  let allocated = 0;
  const adj = counter.map((r) => {
    const a = Math.round(-extra * (Math.abs(r.delta) / totalMag));
    allocated += a;
    return { seatId: r.seatId, a };
  });
  adj[0].a += -extra - allocated;
  const adjMap = new Map(adj.map((x) => [x.seatId, x.a]));

  const results = state.results.map((r) => {
    if (r.seatId === heroSeat.id) return { ...r, delta: r.delta + extra };
    const a = adjMap.get(r.seatId) ?? 0;
    return a ? { ...r, delta: r.delta + a } : r;
  });
  const seats = state.seats.map((s) => {
    if (s.id === heroSeat.id) return { ...s, chips: s.chips + extra };
    const a = adjMap.get(s.id) ?? 0;
    return a ? { ...s, chips: s.chips + a } : s;
  });
  return { ...state, seats, results };
}

/** 本人在分牌阶段弃牌：本人按丢处理后直接结算 */
export function applyHeroFoldSplit(state: GameState): GameState {
  if (state.phase !== 'split') return state;
  const seats = state.seats.map((s) =>
    s.isHero
      ? { ...s, status: 'folded' as const, tag: '丢', tagKind: 'fold' as const }
      : s,
  );
  return settle({ ...state, seats }, false);
}

/** 明牌点数/牌型标签（对手两张明牌） */
function refreshCardHints(state: GameState): GameState {
  const seats = state.seats.map((s) => {
    if (s.isHero || s.status === 'folded') return s;
    const faceUp = s.cards.filter((c) => c.faceUp);
    if (faceUp.length >= 2) {
      const ev = evalGroup([faceUp[0], faceUp[1]]);
      return { ...s, cardHint: ev.name };
    }
    return s;
  });
  return { ...state, seats };
}

/**
 * 结算：分牌比大小，分配底池 + 芒果/皮。
 * earlyFold=true 表示中途只剩一人（皱芒）。
 */
export function settle(state: GameState, earlyFold: boolean): GameState {
  const seats = state.seats.map((s) => ({ ...s, isActive: false }));
  const results: PlayerResult[] = [];

  const aliveIdx = activeContenders(seats);
  let pot = state.pot;
  let mangoPool = state.mangoPool;
  let piPool = state.piPool; // 皮池为各局结转，不凭空铸造

  if (earlyFold || aliveIdx.length === 1) {
    // 皱芒：唯一存活者通吃底池 + 当前芒果
    const wi = aliveIdx[0];
    const winner = seats[wi];
    const gain = pot + mangoPool;
    winner.chips += gain;
    results.push({
      seatId: winner.id,
      name: winner.name,
      delta: gain - winner.totalBet,
      handName: '皱芒 · 通吃',
      won: true,
    });
    seats.forEach((s, i) => {
      if (i !== wi) {
        results.push({
          seatId: s.id,
          name: s.name,
          delta: s.status === 'folded' ? -s.totalBet : 0,
          handName: '丢',
          won: false,
        });
      }
    });
    mangoPool = 0;
    return finishSettle(state, seats, results, 0, mangoPool, piPool, '皱芒：无人应战，庄家通吃');
  }

  // 正常分牌比大小
  const splits = new Map<number, SplitHand>();
  const specials = new Map<number, '三花十' | '三花六'>();
  aliveIdx.forEach((i) => {
    const sp = specialHand(seats[i].cards);
    if (sp) {
      specials.set(i, sp);
    } else if (seats[i].isHero && seats[i].manualHeadIds?.length === 2) {
      splits.set(i, splitFromChosen(seats[i].cards, seats[i].manualHeadIds!));
    } else {
      splits.set(i, bestSplit(seats[i].cards));
    }
  });

  // 三花十/三花六：不输不赢，退回本局投入
  specials.forEach((sp, i) => {
    const s = seats[i];
    pot -= s.totalBet;
    s.chips += s.totalBet;
    results.push({ seatId: s.id, name: s.name, delta: 0, handName: sp, special: sp, won: false });
  });

  const contenders = [...splits.keys()];
  if (contenders.length === 0) {
    return finishSettle(state, seats, results, pot, mangoPool, piPool, '全员特殊牌型，本局不分胜负');
  }

  // 未被任何人击败者为赢家组（平分底池）
  const winners = contenders.filter((i) =>
    contenders.every((j) => j === i || compareSplit(splits.get(j)!, splits.get(i)!) <= 0),
  );
  const share = Math.floor(pot / winners.length);
  let remainder = pot - share * winners.length; // 不能整除的余数给首位赢家

  // 尾牌最大者收皮池 + 芒果池
  let tailTop = contenders[0];
  contenders.forEach((i) => {
    if (
      evalGroup(splits.get(i)!.tail).rank > evalGroup(splits.get(tailTop)!.tail).rank ||
      (splits.get(i)!.tailEval.rank === splits.get(tailTop)!.tailEval.rank &&
        splits.get(i)!.tailEval.topSingle > splits.get(tailTop)!.tailEval.topSingle)
    ) {
      tailTop = i;
    }
  });
  const mangoGain = mangoPool;
  const piGain = piPool;

  contenders.forEach((i) => {
    const s = seats[i];
    const sh = splits.get(i)!;
    const isWinner = winners.includes(i);
    let gain = isWinner ? share : 0;
    if (isWinner && remainder > 0) {
      gain += remainder;
      remainder = 0;
    }
    if (i === tailTop) {
      gain += mangoGain + piGain;
    }
    s.chips += gain;
    results.push({
      seatId: s.id,
      name: s.name,
      delta: gain - s.totalBet,
      handName: `头:${sh.headEval.name} / 尾:${sh.tailEval.name}`,
      won: isWinner,
      headCards: [...sh.head].map((c) => ({ ...c, faceUp: true })),
      tailCards: [...sh.tail].map((c) => ({ ...c, faceUp: true })),
    });
  });
  // 已丢牌者
  seats.forEach((s) => {
    if (s.status === 'folded') {
      results.push({ seatId: s.id, name: s.name, delta: -s.totalBet, handName: '丢', won: false });
    }
  });

  mangoPool = 0;
  piPool = 0;

  // 爆奖：检测奖励牌型（天牌/地牌/人牌），命中则从奖池派奖
  const { jackpot, jackpotHit } = applyJackpot(state.jackpot, seats, contenders, results);

  const winnerNames = winners.map((i) => seats[i].name).join('、');
  return finishSettle(state, seats, results, 0, mangoPool, piPool, `赢家：${winnerNames}`, jackpot, jackpotHit);
}

export const JACKPOT_ACCUM = 200; // 每局向奖池累积的额度

/** 一手 4 张牌中是否含奖励对子（天牌=红Q对 / 地牌=红2对 / 人牌=红8对），返回其牌型名 */
function rewardHandName(cards: Card[], rewardPcts: Map<string, number>): string | null {
  const byRank = new Map<string, Card[]>();
  cards.forEach((c) => {
    if (!isRed(c.suit)) return;
    const arr = byRank.get(c.rank) ?? [];
    arr.push(c);
    byRank.set(c.rank, arr);
  });
  for (const arr of byRank.values()) {
    if (arr.length >= 2) {
      const name = pairName(arr[0]);
      if (rewardPcts.has(name)) return name;
    }
  }
  return null;
}

function applyJackpot(
  jackpot: Jackpot,
  seats: Seat[],
  contenders: number[],
  results: PlayerResult[],
): { jackpot: Jackpot; jackpotHit?: JackpotHit } {
  const pctOf = new Map(jackpot.rewards.map((r) => [r.hand, r.percent]));
  let best: { idx: number; hand: string; percent: number } | null = null;
  contenders.forEach((i) => {
    const name = rewardHandName(seats[i].cards, pctOf);
    if (!name) return;
    const percent = pctOf.get(name)!;
    if (!best || percent > best.percent) {
      best = { idx: i, hand: name, percent };
    }
  });

  if (!best) return { jackpot };
  const hit = best as { idx: number; hand: string; percent: number };
  const amount = Math.floor((jackpot.total * hit.percent) / 100);
  const s = seats[hit.idx];
  s.chips += amount;
  const r = results.find((x) => x.seatId === s.id);
  if (r) r.delta += amount;

  const date = new Date().toLocaleDateString('zh-CN');
  const record = { player: s.name, hand: hit.hand, amount, date };
  const topWinner = amount > jackpot.topWinner.amount ? record : jackpot.topWinner;
  const newJackpot: Jackpot = {
    ...jackpot,
    total: Math.max(0, jackpot.total - amount),
    topWinner,
    records: [record, ...jackpot.records].slice(0, 6),
  };
  const jackpotHit: JackpotHit = {
    seatId: s.id,
    player: s.name,
    hand: hit.hand,
    percent: hit.percent,
    amount,
  };
  return { jackpot: newJackpot, jackpotHit };
}

function finishSettle(
  state: GameState,
  seats: Seat[],
  results: PlayerResult[],
  pot: number,
  mangoPool: number,
  piPool: number,
  message: string,
  jackpot?: Jackpot,
  jackpotHit?: JackpotHit,
): GameState {
  // 给每位玩家打上结算徽标
  const decorated = seats.map((s) => {
    const r = results.find((x) => x.seatId === s.id);
    if (r && r.won) return { ...s, tag: '赢', tagKind: 'split' as const };
    return s;
  });
  // 奖池随每局累积
  const base = jackpot ?? state.jackpot;
  const grownJackpot: Jackpot = { ...base, total: base.total + JACKPOT_ACCUM };
  return {
    ...state,
    seats: decorated,
    pot,
    mangoPool,
    piPool,
    phase: 'settle',
    actorIndex: -1,
    results,
    message,
    jackpot: grownJackpot,
    jackpotHit,
  };
}

/** 估算一手牌当前最强 2 张组牌的排序值 */
function bestGroupRank(cards: Card[]): number {
  if (cards.length >= 4) return bestSplit(cards).headEval.rank;
  let best = 0;
  for (let i = 0; i < cards.length; i++) {
    for (let j = i + 1; j < cards.length; j++) {
      best = Math.max(best, evalGroup([cards[i], cards[j]]).rank);
    }
  }
  return best;
}

/** AI 决策：返回动作与（加注时的）增量 */
export function aiAction(state: GameState): { action: BetAction; amount?: number } {
  const idx = state.actorIndex;
  const seat = state.seats[idx];
  const strength = Math.min(1, Math.max(0, (bestGroupRank(seat.cards) - 100) / 900));
  const r = Math.random();
  const canKnock = state.bobo - seat.totalBet <= state.baseChip * 2;

  // 第 1 轮「必发招｜可休」：只能休或丢，不开放下注
  if (state.round === 1) {
    if (strength < 0.18 && r < 0.5) return { action: 'fold' };
    return { action: 'rest' };
  }

  if (state.currentBet === 0) {
    // 本轮尚无人下注：可休 / 大 / 敲
    if (strength > 0.82 && r < 0.4) return { action: 'knock' };
    if (strength > 0.5) return { action: 'raise', amount: state.baseChip * (strength > 0.7 ? 2 : 1) };
    return { action: 'rest' };
  }

  const toCall = state.currentBet - seat.betThisRound;
  const tooExpensive = toCall > state.bobo * 0.5;
  if (strength > 0.85 && canKnock && r < 0.5) return { action: 'knock' };
  if (strength > 0.7) {
    if (r < 0.35 && seat.totalBet + toCall < state.bobo) {
      return { action: 'raise', amount: state.baseChip * 2 };
    }
    return { action: 'call' };
  }
  if (strength > 0.4) {
    return tooExpensive && r < 0.5 ? { action: 'fold' } : { action: 'call' };
  }
  // 弱牌：多数丢，偶尔跟小注
  if (toCall === 0) return { action: 'call' };
  return r < 0.75 ? { action: 'fold' } : { action: 'call' };
}

/** 本人牌型提示（4 张自动分墩） */
export function heroHandHint(state: GameState): string {
  const hero = state.seats.find((s) => s.isHero);
  if (!hero || hero.cards.length < 4) return '';
  const sp = specialHand(hero.cards);
  if (sp) return sp;
  const split = bestSplit(hero.cards);
  return `${split.headEval.name} / ${split.tailEval.name}`;
}
