/**
 * 按与后端 DiceRewardLogic 一致的环形规则，为盘面 26 格（id 0～25）求档位 tier，
 * 并生成 real_ev / ui 等字段。
 *
 * 摇取点数为 5～30：起点为 grid_number==摇取点数 的格位下标，顺时针落点 (start+摇取)%26，
 * 逆时针落点 start-摇取（<0 则 +26）。
 */

export type IndexTier = 'T1' | 'T2' | 'T3' | 'T4' | 'T5'

/** 豹子摇取点数：这些摇取下落点不得为 T4/T5（与 dice_reward 表中「点数」列一致） */
export const LEOPARD_ROLLS: readonly number[] = [5, 10, 15, 20, 25, 30]

const BOARD_SIZE = 26
const GRID_MIN = 5
const GRID_MAX = 30

const T_ALL: IndexTier[] = ['T1', 'T2', 'T3', 'T4', 'T5']
const T_NO_LEOPARD: IndexTier[] = ['T1', 'T2', 'T3']
export interface BoardFrequencies {
  grids: number[]
  freqCw: number[]
  freqCcw: number[]
  leopardLandCw: Set<number>
  leopardLandCcw: Set<number>
  leopardLandUnion: Set<number>
}

/**
 * 条数约束：T1/T4/T5 为「恰好」；T2 为「不少于」（加权条数，与 dice_reward 中顺/逆各 26 条摇取结果一致）
 */
export interface TierCountConstraints {
  t1FixedCw: number
  t2MinCw: number
  /** 顺时针方向 T4 加权条数固定为该值 */
  t4FixedCw: number
  /** 顺时针方向 T5 加权条数固定为该值 */
  t5FixedCw: number
  t1FixedCcw: number
  t2MinCcw: number
  /** 逆时针方向 T4 加权条数固定为该值 */
  t4FixedCcw: number
  /** 逆时针方向 T5 加权条数固定为该值 */
  t5FixedCcw: number
}

/** 各档位统一 real_ev 标准（生成 DiceRewardConfig 时使用；细则可再到表格里改） */
export interface TierRealEvStandards {
  T1: number
  T2: number
  T3: number
  T4: number
  T5: number
}

/** 默认推荐结算金额（满足档位区间规则，可在页面修改） */
export const DEFAULT_TIER_REAL_EV_STANDARDS: TierRealEvStandards = {
  T1: 3,
  T2: 1.5,
  T3: 0.5,
  T4: -0.4,
  T5: 0
}

/**
 * 按结算金额推断档位（与奖励配置页规则说明一致）
 * T1 大奖：>2
 * T2 小赚：2>=金额>1
 * T3 抽水：1>=金额>0
 * T4 惩罚：0>金额
 * T5 再来一次：=0
 */
/** 各档位默认备注（与结算金额推断档位规则一致，修改结算金额时实时同步） */
export const TIER_REMARK_BY_TIER: Record<IndexTier, string> = {
  T1: '大奖',
  T2: '小赚',
  T3: '抽水',
  T4: '惩罚',
  T5: '再来一次'
}

export function defaultRemarkForTier(tier: IndexTier | 'BIGWIN' | string): string {
  if (tier === 'BIGWIN' || tier === 'T1') {
    return TIER_REMARK_BY_TIER.T1
  }
  if (tier === 'T2' || tier === 'T3' || tier === 'T4' || tier === 'T5') {
    return TIER_REMARK_BY_TIER[tier]
  }
  return ''
}

export function inferTierFromRealEv(realEv: number): IndexTier | '' {
  if (!Number.isFinite(realEv)) {
    return ''
  }
  if (realEv === 0) {
    return 'T5'
  }
  if (realEv < 0) {
    return 'T4'
  }
  if (realEv > 2) {
    return 'T1'
  }
  if (realEv > 1 && realEv <= 2) {
    return 'T2'
  }
  if (realEv > 0 && realEv <= 1) {
    return 'T3'
  }
  return ''
}

/**
 * 校验档位与 real_ev 区间是否一致；通过返回 null，否则返回 i18n 键名（不含 page.configPage. 前缀）
 */
export function validateTierRealEvStandards(s: TierRealEvStandards): string | null {
  if (!Number.isFinite(s.T1) || !(s.T1 > 2)) {
    return 'ruleGenInvalidT1RealEv'
  }
  if (!Number.isFinite(s.T2) || !(s.T2 > 1 && s.T2 <= 2)) {
    return 'ruleGenInvalidT2RealEv'
  }
  if (!Number.isFinite(s.T3) || !(s.T3 > 0 && s.T3 <= 1)) {
    return 'ruleGenInvalidT3RealEv'
  }
  if (!Number.isFinite(s.T4) || !(s.T4 < 0)) {
    return 'ruleGenInvalidT4RealEv'
  }
  if (!Number.isFinite(s.T5) || s.T5 !== 0) {
    return 'ruleGenInvalidT5RealEv'
  }
  return null
}

export interface GenerateTierInput {
  grids: number[]
  constraints: TierCountConstraints
}

export interface GenerateTierResultOk {
  ok: true
  tiers: IndexTier[]
}

export interface GenerateTierResultFail {
  ok: false
  message: string
}

export type GenerateTierResult = GenerateTierResultOk | GenerateTierResultFail

export function computeBoardFrequencies(grids: number[]): BoardFrequencies | null {
  if (grids.length !== BOARD_SIZE) {
    return null
  }
  const gridToPos: Record<number, number> = {}
  for (let pos = 0; pos < BOARD_SIZE; pos++) {
    const g = grids[pos]
    if (g < GRID_MIN || g > GRID_MAX) {
      return null
    }
    if (gridToPos[g] !== undefined) {
      return null
    }
    gridToPos[g] = pos
  }
  const freqCw = new Array<number>(BOARD_SIZE).fill(0)
  const freqCcw = new Array<number>(BOARD_SIZE).fill(0)
  for (let roll = GRID_MIN; roll <= GRID_MAX; roll++) {
    const startPos = gridToPos[roll]
    const endCw = (startPos + roll) % BOARD_SIZE
    const endCcw = startPos - roll >= 0 ? startPos - roll : BOARD_SIZE + startPos - roll
    freqCw[endCw]++
    freqCcw[endCcw]++
  }
  const leopardLandCw = new Set<number>()
  const leopardLandCcw = new Set<number>()
  for (let di = 0; di < LEOPARD_ROLLS.length; di++) {
    const d = LEOPARD_ROLLS[di]
    const sp = gridToPos[d]
    leopardLandCw.add((sp + d) % BOARD_SIZE)
    const eccw = sp - d >= 0 ? sp - d : BOARD_SIZE + sp - d
    leopardLandCcw.add(eccw)
  }
  const leopardLandUnion = new Set<number>()
  leopardLandCw.forEach((x) => leopardLandUnion.add(x))
  leopardLandCcw.forEach((x) => leopardLandUnion.add(x))
  return {
    grids: [...grids],
    freqCw,
    freqCcw,
    leopardLandCw,
    leopardLandCcw,
    leopardLandUnion
  }
}

function sumWeighted(freq: number[], tiers: IndexTier[], tier: IndexTier): number {
  let s = 0
  for (let i = 0; i < BOARD_SIZE; i++) {
    if (tiers[i] === tier) {
      s += freq[i]
    }
  }
  return s
}

function meetsConstraints(
  tiers: IndexTier[],
  freqCw: number[],
  freqCcw: number[],
  c: TierCountConstraints
): boolean {
  const cw1 = sumWeighted(freqCw, tiers, 'T1')
  const cw2 = sumWeighted(freqCw, tiers, 'T2')
  const cw4 = sumWeighted(freqCw, tiers, 'T4')
  const cw5 = sumWeighted(freqCw, tiers, 'T5')
  const cc1 = sumWeighted(freqCcw, tiers, 'T1')
  const cc2 = sumWeighted(freqCcw, tiers, 'T2')
  const cc4 = sumWeighted(freqCcw, tiers, 'T4')
  const cc5 = sumWeighted(freqCcw, tiers, 'T5')
  return (
    cw1 === c.t1FixedCw &&
    cw2 >= c.t2MinCw &&
    cw4 === c.t4FixedCw &&
    cw5 === c.t5FixedCw &&
    cc1 === c.t1FixedCcw &&
    cc2 >= c.t2MinCcw &&
    cc4 === c.t4FixedCcw &&
    cc5 === c.t5FixedCcw
  )
}

function nonLeopardTierChoices(c: TierCountConstraints): IndexTier[] {
  const out: IndexTier[] = ['T1', 'T2', 'T3']
  if (c.t4FixedCw > 0 || c.t4FixedCcw > 0) {
    out.push('T4')
  }
  if (c.t5FixedCw > 0 || c.t5FixedCcw > 0) {
    out.push('T5')
  }
  return out
}

function shuffleIndices(rand: () => number): number[] {
  const a: number[] = []
  for (let i = 0; i < BOARD_SIZE; i++) {
    a.push(i)
  }
  for (let i = BOARD_SIZE - 1; i > 0; i--) {
    const j = Math.floor(rand() * (i + 1))
    const t = a[i]
    a[i] = a[j]
    a[j] = t
  }
  return a
}

function mulberry32(seed: number): () => number {
  return () => {
    let t = (seed += 0x6d2b79f5)
    t = Math.imul(t ^ (t >>> 15), t | 1)
    t ^= t + Math.imul(t ^ (t >>> 7), t | 61)
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296
  }
}

/**
 * 随机搜索可行档位：豹子落点禁 T4/T5；T4/T5 顺/逆加权条数分别等于约束中的固定值（可多条 T4/T5 格位）。
 */
export function generateTiers(input: GenerateTierInput): GenerateTierResult {
  const board = computeBoardFrequencies(input.grids)
  if (board === null) {
    return { ok: false, message: 'grid_number 须为 5～30 各出现一次且共 26 条' }
  }

  const { freqCw, freqCcw, leopardLandUnion } = board
  const c = input.constraints

  let seed = 0
  for (let i = 0; i < BOARD_SIZE; i++) {
    seed = (seed + input.grids[i] * 31 + i) | 0
  }
  const rand = mulberry32(seed === 0 ? 0x9e3779b9 : seed)

  const needT5Cell = c.t5FixedCw > 0 || c.t5FixedCcw > 0
  if (needT5Cell) {
    let hasNonLeopard = false
    for (let i = 0; i < BOARD_SIZE; i++) {
      if (!leopardLandUnion.has(i)) {
        hasNonLeopard = true
        break
      }
    }
    if (!hasNonLeopard) {
      return { ok: false, message: '无可用 T5 格位（豹子摇取落点占满全盘，请调整 grid_number 排布）' }
    }
  }

  const nonLeopardChoices = nonLeopardTierChoices(c)
  const maxAttempts = 400000
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    const tiers: IndexTier[] = new Array(BOARD_SIZE).fill('T3')
    const order = shuffleIndices(rand)
    for (let oi = 0; oi < order.length; oi++) {
      const pos = order[oi]
      if (leopardLandUnion.has(pos)) {
        tiers[pos] = T_NO_LEOPARD[Math.floor(rand() * T_NO_LEOPARD.length)]
      } else {
        tiers[pos] = nonLeopardChoices[Math.floor(rand() * nonLeopardChoices.length)]
      }
    }

    if (meetsConstraints(tiers, freqCw, freqCcw, c)) {
      return { ok: true, tiers }
    }
  }

  return { ok: false, message: '在当前盘面与约束下未找到可行解，请调整 T1/T4/T5 固定条数或放宽 T2 下限后重试' }
}

function uiTextByTierWhenStandards(
  tier: IndexTier,
  realEv: number
): { ui_text: string; ui_text_en: string } {
  if (tier === 'T5') {
    return { ui_text: '再来一次', ui_text_en: 'Once again' }
  }
  const value = Number.isFinite(realEv) ? realEv.toFixed(2) : String(realEv)
  return { ui_text: value, ui_text_en: value }
}

/** 展示文案：直接使用真实结算值（中英文相同） */
function uiTextFromRealEv(realEv: number): { ui_text: string; ui_text_en: string } {
  const value = Number.isFinite(realEv) ? realEv.toFixed(2) : String(realEv)
  return { ui_text: value, ui_text_en: value }
}

/**
 * 按 tier 生成展示字段。
 * @param standards 若传入，则各档位统一使用对应 real_ev 标准；不传则使用内置随机占位（兼容脚本/旧逻辑）
 */
export function buildRowsFromTiers(
  grids: number[],
  tiers: IndexTier[],
  standards?: TierRealEvStandards
): Array<{
  id: number
  grid_number: number
  ui_text: string
  ui_text_en: string
  real_ev: number
  tier: IndexTier
  remark: string
}> {
  const rows: Array<{
    id: number
    grid_number: number
    ui_text: string
    ui_text_en: string
    real_ev: number
    tier: IndexTier
    remark: string
  }> = []
  let t4Seq = 0
  for (let id = 0; id < BOARD_SIZE; id++) {
    const tier = tiers[id]
    const grid_number = grids[id]
    let ui_text = ''
    let ui_text_en = ''
    let real_ev = 0
    let remark = ''

    if (standards !== undefined) {
      if (tier === 'T1') {
        real_ev = standards.T1
        const f = uiTextByTierWhenStandards(tier, real_ev)
        ui_text = f.ui_text
        ui_text_en = f.ui_text_en
        remark = '大奖格'
      } else if (tier === 'T2') {
        real_ev = standards.T2
        const f = uiTextByTierWhenStandards(tier, real_ev)
        ui_text = f.ui_text
        ui_text_en = f.ui_text_en
        remark = standards.T2 <= 1 ? '完美回本' : '小赚'
      } else if (tier === 'T3') {
        real_ev = standards.T3
        const f = uiTextByTierWhenStandards(tier, real_ev)
        ui_text = f.ui_text
        ui_text_en = f.ui_text_en
        remark = '抽水'
      } else if (tier === 'T4') {
        real_ev = standards.T4
        const f = uiTextByTierWhenStandards(tier, real_ev)
        ui_text = f.ui_text
        ui_text_en = f.ui_text_en
        remark = '惩罚'
      } else {
        real_ev = standards.T5
        const f = uiTextByTierWhenStandards(tier, real_ev)
        ui_text = f.ui_text
        ui_text_en = f.ui_text_en
        remark = '再来一次'
      }
    } else if (tier === 'T1') {
      real_ev = 101 + ((id * 17 + grid_number * 3) % 398)
      if (real_ev >= 500) {
        real_ev = 498
      }
      const f = uiTextFromRealEv(real_ev)
      ui_text = f.ui_text
      ui_text_en = f.ui_text_en
      remark = '大奖格'
    } else if (tier === 'T2') {
      if (id % 3 === 0) {
        real_ev = 1
        remark = '完美回本'
      } else {
        real_ev = 20 + ((id * 11) % 75)
        if (real_ev <= 0) {
          real_ev = 50
        }
        remark = '小赚'
      }
      const f = uiTextFromRealEv(real_ev)
      ui_text = f.ui_text
      ui_text_en = f.ui_text_en
    } else if (tier === 'T3') {
      real_ev = -72 - (id % 15)
      const f = uiTextFromRealEv(real_ev)
      ui_text = f.ui_text
      ui_text_en = f.ui_text_en
      remark = '抽水'
    } else if (tier === 'T4') {
      t4Seq++
      real_ev = -101 - t4Seq * 15
      const f = uiTextFromRealEv(real_ev)
      ui_text = f.ui_text
      ui_text_en = f.ui_text_en
      remark = '惩罚'
    } else {
      real_ev = 0
      ui_text = '再来一次'
      ui_text_en = 'Once again'
      remark = '再来一次'
    }
    rows.push({
      id,
      grid_number,
      ui_text,
      ui_text_en,
      real_ev,
      tier,
      remark
    })
  }
  return rows
}

export function summarizeCounts(
  board: BoardFrequencies,
  tiers: IndexTier[]
): { cw: Record<IndexTier, number>; ccw: Record<IndexTier, number> } {
  const cw: Record<IndexTier, number> = { T1: 0, T2: 0, T3: 0, T4: 0, T5: 0 }
  const ccw: Record<IndexTier, number> = { T1: 0, T2: 0, T3: 0, T4: 0, T5: 0 }
  for (let k = 0; k < T_ALL.length; k++) {
    const t = T_ALL[k]
    cw[t] = sumWeighted(board.freqCw, tiers, t)
    ccw[t] = sumWeighted(board.freqCcw, tiers, t)
  }
  return { cw, ccw }
}
