import './styles.css'

type Tile = { id: number; kind: number; removed: boolean }
type Point = { r: number; c: number }

const assets = Array.from({ length: 20 }, (_, index) => `/avatars/avatar-${String(index + 1).padStart(2, '0')}.png?v=4`)
const popupAssets = {
  timeout: '/popups/timeout.png',
  oneHeart: '/popups/frame-33.png',
  twoHearts: '/popups/frame-34.png',
  final: '/popups/frame-25.png',
}
const levels = [
  { stars: 1, rows: 4, cols: 4, seconds: 85, label: '轻松入门' },
  { stars: 2, rows: 4, cols: 6, seconds: 85, label: '逐步熟悉' },
  { stars: 5, rows: 5, cols: 8, seconds: 85, label: '极限挑战' },
  { stars: 4, rows: 4, cols: 8, seconds: 85, label: '障碍迷阵' },
  { stars: 4, rows: 5, cols: 8, seconds: 85, label: '连击大师' },
]

const app = document.querySelector<HTMLDivElement>('#app')!
let levelIndex = 0
let board: Tile[][] = []
let selected: Point | null = null
let score = 0
let timeLeft = levels[0].seconds
let timer: number | undefined
let locked = false
let hintTimer: number | undefined
let autoAdvanceTimer: number | undefined
let completedCount = 0
let hintsLeft = 3
let shufflesLeft = 3

function level() { return levels[levelIndex] }
function formatTime(total: number) { return `${String(Math.floor(total / 60)).padStart(2, '0')}:${String(total % 60).padStart(2, '0')}` }
function hearts(count: number) { return Array.from({ length: 5 }, (_, i) => `<span class="heart ${i < count ? 'filled' : ''}" aria-hidden="true"></span>`).join('') }

function renderShell() {
  app.innerHTML = `
    <main class="game-page">
      <header class="topbar">
        <div class="brand-block"><span class="brand-mark"><img src="/avatar.jpeg" alt="ibo头像" /></span><div><strong>ibo连连看</strong><small>考验ibo的时候到了</small></div></div>
        <button class="icon-btn" id="restart" aria-label="重新开始">↻</button>
      </header>
      <section class="reward-panel" aria-label="奖励进度">
        <div class="reward-heading"><span>奖励进度</span><b id="reward-label">0 / 5</b></div>
        <div class="progress-track"><div class="progress-fill" id="progress-fill"></div><div class="reward-dots" id="reward-dots"></div></div>
      </section>
      <section class="level-heading"><div><span class="eyebrow">LEVEL <b id="level-number">01</b></span><h1 id="level-title"></h1></div><div class="hearts" id="level-hearts" aria-label="关卡难度"></div></section>
      <section class="status-row"><div class="status-card"><small>剩余时间</small><strong id="time">00:00</strong></div><div class="status-card"><small>本局得分</small><strong id="score">0000</strong></div><div class="status-card"><small>剩余图案</small><strong id="remaining">00</strong></div></section>
      <section class="board-wrap"><div class="board" id="board" role="grid" aria-label="连连看棋盘"></div><canvas id="path-canvas"></canvas></section>
      <div class="tool-row"><button class="tool-btn" id="hint"><span>✦</span>提示 <em id="hint-count">3</em></button><button class="tool-btn" id="shuffle"><span>⤨</span>洗牌 <em id="shuffle-count">3</em></button></div>
      <p class="tip-line">找到相同的图案，最多转两个弯即可消除</p>
    </main>
    <div class="toast" id="toast" role="status" aria-live="polite"></div>
    <div class="modal-backdrop hidden" id="modal"><section class="reward-modal"><h2 id="modal-title">完成啦</h2><p id="modal-copy"></p><div class="reward-art" id="reward-art"></div><button class="primary-btn" id="modal-action">继续挑战</button></section></div>
  `
}

function createBoard() {
  const { rows, cols } = level()
  hintsLeft = 3
  shufflesLeft = 3
  const pairs = rows * cols / 2
  if (!Number.isInteger(pairs) || pairs > assets.length) throw new Error(`关卡 ${levelIndex + 1} 的棋盘配置无效`)
  const kinds = Array.from({ length: pairs }, (_, i) => i)
  const values = [...kinds, ...kinds].sort(() => Math.random() - 0.5)
  board = Array.from({ length: rows }, (_, r) => Array.from({ length: cols }, (_, c) => ({ id: r * cols + c, kind: values[r * cols + c], removed: false })))
  ensurePlayableBoard()
  selected = null
  drawBoard()
}

function hasAvailableMove() {
  const active = board.flatMap((row, r) => row.map((tile, c) => tile.removed ? null : ({ ...tile, r, c }))).filter(Boolean) as (Tile & Point)[]
  for (let i = 0; i < active.length; i++) for (let j = i + 1; j < active.length; j++) {
    if (active[i].kind === active[j].kind && findPath(active[i], active[j])) return true
  }
  return false
}

function ensurePlayableBoard() {
  const activeTiles = () => board.flat().filter(tile => !tile.removed)
  let attempts = 0
  while (activeTiles().length > 1 && !hasAvailableMove() && attempts < 80) {
    const kinds = activeTiles().map(tile => tile.kind).sort(() => Math.random() - 0.5)
    let index = 0
    board.forEach(row => row.forEach(tile => { if (!tile.removed) tile.kind = kinds[index++] }))
    attempts++
  }
}

function drawBoard() {
  const el = document.querySelector<HTMLDivElement>('#board')!
  el.style.gridTemplateColumns = `repeat(${level().cols}, minmax(0, 1fr))`
  el.innerHTML = board.flatMap((row, r) => row.map((tile, c) => tile.removed ? `<button class="tile empty" aria-hidden="true"></button>` : `<button class="tile" data-r="${r}" data-c="${c}" aria-label="图案 ${tile.kind + 1}"><img src="${assets[tile.kind]}" alt="" draggable="false" /></button>`)).join('')
  el.querySelectorAll<HTMLButtonElement>('.tile:not(.empty)').forEach(tile => tile.addEventListener('click', () => choose(Number(tile.dataset.r), Number(tile.dataset.c))))
  updateStats()
}

function updateStats() {
  document.querySelector('#level-number')!.textContent = String(levelIndex + 1).padStart(2, '0')
  document.querySelector('#level-title')!.textContent = level().label
  document.querySelector('#level-hearts')!.innerHTML = hearts(level().stars)
  document.querySelector('#score')!.textContent = String(score).padStart(4, '0')
  document.querySelector('#time')!.textContent = formatTime(timeLeft)
  document.querySelector('#remaining')!.textContent = String(board.flat().filter(t => !t.removed).length).padStart(2, '0')
  document.querySelector('#reward-label')!.textContent = `${completedCount} / 5`
  document.querySelector('#progress-fill')!.style.width = `${completedCount * 20}%`
  document.querySelector('#reward-dots')!.innerHTML = levels.map((_, i) => `<span class="reward-dot ${i < completedCount ? 'done' : ''} ${i === completedCount ? 'current' : ''}">${i < completedCount ? '✓' : i + 1}</span>`).join('')
  const hintButton = document.querySelector<HTMLButtonElement>('#hint')!
  const shuffleButton = document.querySelector<HTMLButtonElement>('#shuffle')!
  document.querySelector('#hint-count')!.textContent = String(hintsLeft)
  document.querySelector('#shuffle-count')!.textContent = String(shufflesLeft)
  hintButton.disabled = hintsLeft === 0
  shuffleButton.disabled = shufflesLeft === 0
}

function choose(r: number, c: number) {
  if (locked || board[r][c].removed) return
  const buttons = document.querySelectorAll<HTMLButtonElement>('.tile:not(.empty)')
  buttons.forEach(b => b.classList.remove('selected'))
  if (!selected) { selected = { r, c }; buttonAt(r, c)?.classList.add('selected'); return }
  const first = selected; selected = null
  if (first.r === r && first.c === c) return
  if (board[first.r][first.c].kind !== board[r][c].kind) { buttonAt(r, c)?.classList.add('wrong'); setTimeout(() => buttonAt(r, c)?.classList.remove('wrong'), 260); showToast('图案不一样，再试试相同的头像'); return }
  const path = findPath(first, { r, c })
  if (!path) { buttonAt(r, c)?.classList.add('wrong'); setTimeout(() => buttonAt(r, c)?.classList.remove('wrong'), 260); showToast('这两张暂时连不上，最多只能转两个弯'); return }
  locked = true
  drawPath(path)
  setTimeout(() => { board[first.r][first.c].removed = true; board[r][c].removed = true; score += 100 + Math.max(0, timeLeft); locked = false; clearPath(); showToast('连接成功 + 分数'); if (board.flat().every(t => t.removed)) { drawBoard(); finishLevel(); return } const before = board.flat().map(t => t.kind).join(','); ensurePlayableBoard(); const after = board.flat().map(t => t.kind).join(','); if (before !== after) showToast('棋盘已自动整理，继续消除'); drawBoard() }, 240)
}

function buttonAt(r: number, c: number) { return document.querySelector<HTMLButtonElement>(`.tile[data-r="${r}"][data-c="${c}"]`) }

function findPath(start: Point, end: Point): Point[] | null {
  const rows = level().rows, cols = level().cols
  const grid = Array.from({ length: rows + 2 }, (_, r) => Array.from({ length: cols + 2 }, (_, c) => r > 0 && r <= rows && c > 0 && c <= cols && !board[r - 1][c - 1].removed && !(r - 1 === start.r && c - 1 === start.c) && !(r - 1 === end.r && c - 1 === end.c)))
  const dirs = [[-1,0],[0,1],[1,0],[0,-1]]
  const queue: { r: number; c: number; dir: number; turns: number; path: Point[] }[] = [{ r: start.r + 1, c: start.c + 1, dir: -1, turns: 0, path: [{ r: start.r, c: start.c }] }]
  const seen = new Map<string, number>()
  while (queue.length) {
    const node = queue.shift()!
    for (let d = 0; d < 4; d++) {
      const turns = node.dir === -1 || node.dir === d ? node.turns : node.turns + 1
      if (turns > 2) continue
      const nr = node.r + dirs[d][0], nc = node.c + dirs[d][1]
      if (nr < 0 || nr > rows + 1 || nc < 0 || nc > cols + 1 || grid[nr][nc]) continue
      const key = `${nr},${nc},${d}`
      if ((seen.get(key) ?? 99) <= turns) continue
      seen.set(key, turns)
      const nextPath = node.path.concat(nr > 0 && nr <= rows && nc > 0 && nc <= cols ? [{ r: nr - 1, c: nc - 1 }] : [])
      if (nr === end.r + 1 && nc === end.c + 1) return nextPath
      queue.push({ r: nr, c: nc, dir: d, turns, path: nextPath })
    }
  }
  return null
}

function drawPath(path: Point[]) { const canvas = document.querySelector<HTMLCanvasElement>('#path-canvas')!, boardEl = document.querySelector<HTMLDivElement>('#board')!, rect = boardEl.getBoundingClientRect(); canvas.width = rect.width * devicePixelRatio; canvas.height = rect.height * devicePixelRatio; canvas.style.width = `${rect.width}px`; canvas.style.height = `${rect.height}px`; const ctx = canvas.getContext('2d')!; ctx.scale(devicePixelRatio, devicePixelRatio); ctx.strokeStyle = '#267653'; ctx.lineWidth = 4; ctx.lineCap = 'round'; ctx.beginPath(); path.forEach((p, i) => { const x = (p.c + .5) * rect.width / level().cols, y = (p.r + .5) * rect.height / level().rows; i ? ctx.lineTo(x, y) : ctx.moveTo(x, y) }); ctx.stroke() }
function clearPath() { const c = document.querySelector<HTMLCanvasElement>('#path-canvas')!; c.width = 1; c.height = 1 }

function startTimer() { clearInterval(timer); timer = window.setInterval(() => { if (locked) return; timeLeft--; updateStats(); if (timeLeft <= 0) { clearInterval(timer); showModal('时间到啦', '你加油啊', '继续挑战', false, imageArt(popupAssets.timeout, '超时配图')) } }, 1000) }
function imageArt(src: string, alt: string) { return `<img class="popup-image" src="${src}" alt="${alt}" />` }
function rewardArt(levelNumber: number) {
  if (levelNumber === 0) return `<img class="popup-image" src="${popupAssets.oneHeart}" alt="一颗绿色桃心奖励" />`
  if (levelNumber === 1) return `<img class="popup-image" src="${popupAssets.twoHearts}" alt="两颗绿色桃心奖励" />`
  if (levelNumber === 4) return `<img class="popup-image" src="${popupAssets.final}" alt="最终绿色桃心奖励" />`
  return `<div class="modal-hearts">${Array.from({ length: levelNumber + 1 }, () => '<span class="modal-heart"></span>').join('')}</div>`
}
function finishLevel() {
  clearInterval(timer); completedCount = Math.max(completedCount, levelIndex + 1); updateStats()
  const final = levelIndex === 4
  const title = final ? '恭喜你荣获“鹰眼”称号' : `第 ${levelIndex + 1} 关完成`
  const copy = final ? '凭你的手速，一定可以买到演唱会门票' : '恭喜你获得绿色能量'
  showModal(title, copy, '继续挑战', true, rewardArt(levelIndex))
}
function showModal(title: string, copy: string, action: string, completed: boolean, art: string) {
  const modal = document.querySelector('#modal')!; clearTimeout(autoAdvanceTimer)
  document.querySelector('#modal-title')!.textContent = title
  document.querySelector('#modal-copy')!.textContent = copy
  document.querySelector('#modal-action')!.textContent = action
  document.querySelector('#reward-art')!.innerHTML = art.trim().startsWith('<') ? art : imageArt(art, '弹窗配图')
  modal.classList.remove('hidden', 'fly-out'); (modal as HTMLElement).dataset.completed = String(completed)
  if (completed && levelIndex !== 4) autoAdvanceTimer = window.setTimeout(() => nextLevel(), 2000)
}

function nextLevel() { clearTimeout(autoAdvanceTimer); const modal = document.querySelector('#modal')!; const completed = modal.getAttribute('data-completed') === 'true'; if (completed) { modal.classList.add('fly-out'); setTimeout(() => { modal.classList.add('hidden'); levelIndex = levelIndex === 4 ? 0 : levelIndex + 1; if (levelIndex === 0) { score = 0; completedCount = 0 } timeLeft = level().seconds; createBoard(); updateStats(); startTimer() }, 320) } else { modal.classList.add('hidden'); timeLeft = level().seconds; createBoard(); startTimer() } }
function hint() {
  if (locked || hintsLeft === 0) return
  hintsLeft--
  updateStats()
  const tiles = board.flatMap((row, r) => row.map((t, c) => t.removed ? null : ({ ...t, r, c }))).filter(Boolean) as (Tile & Point)[]
  for (let i = 0; i < tiles.length; i++) for (let j = i + 1; j < tiles.length; j++) if (tiles[i].kind === tiles[j].kind && findPath(tiles[i], tiles[j])) {
    const a = buttonAt(tiles[i].r, tiles[i].c), b = buttonAt(tiles[j].r, tiles[j].c)
    a?.classList.add('hinted'); b?.classList.add('hinted')
    setTimeout(() => { a?.classList.remove('hinted'); b?.classList.remove('hinted') }, 1200)
    return
  }
  showToast('当前没有可提示的图案')
}
function shuffle() {
  if (locked || shufflesLeft === 0) return
  shufflesLeft--
  const active = board.flat().filter(t => !t.removed).map(t => t.kind).sort(() => Math.random() - .5)
  let i = 0
  board.forEach(row => row.forEach(t => { if (!t.removed) t.kind = active[i++] }))
  ensurePlayableBoard(); drawBoard(); showToast('棋盘已重新整理')
}
function showToast(message: string) { const toast = document.querySelector<HTMLDivElement>('#toast')!; toast.textContent = message; toast.classList.add('visible'); window.clearTimeout(Number(toast.dataset.timer)); toast.dataset.timer = String(window.setTimeout(() => toast.classList.remove('visible'), 1500)) }

renderShell(); createBoard(); startTimer();
document.querySelector('#restart')!.addEventListener('click', () => { timeLeft = level().seconds; createBoard(); startTimer() })
document.querySelector('#hint')!.addEventListener('click', hint)
document.querySelector('#shuffle')!.addEventListener('click', shuffle)
document.querySelector('#modal-action')!.addEventListener('click', nextLevel)
window.addEventListener('resize', clearPath)
