import { CW, CH, OY, RH } from '../config';
import type { Game } from '../core/Game';
import type { Renderer } from './Renderer';

/**
 * HUD и миникарта на прозрачном 2D-канвасе ПОВЕРХ WebGL-холста.
 * Текст и тонкие линии в Canvas2D остаются чёткими и их просто стилизовать —
 * куда удобнее, чем тянуть шрифты в WebGL. Чисто отрисовка, без логики.
 */
export class HudOverlay implements Renderer {
  private readonly ctx: CanvasRenderingContext2D;
  private readonly images = new Map<string, HTMLImageElement>();
  private readonly assetBasePath: string;

  /** Лениво грузит PNG из выбранного пака; возвращает картинку, только когда она готова. */
  private img(name: string): HTMLImageElement | null {
    let im = this.images.get(name);
    if (!im) {
      im = new Image();
      im.src = `${this.assetBasePath}/${name}.png`;
      this.images.set(name, im);
    }
    return im.complete && im.naturalWidth > 0 ? im : null;
  }

  constructor(canvas: HTMLCanvasElement, assetBasePath = 'assets') {
    this.assetBasePath = assetBasePath;
    // Буфер увеличиваем под плотность пикселей (чёткий текст на HiDPI),
    // а рисуем по-прежнему в логических координатах CW×CH.
    const dpr = Math.min(window.devicePixelRatio || 1, 2);
    canvas.width = CW * dpr;
    canvas.height = CH * dpr;
    this.ctx = canvas.getContext('2d')!;
    this.ctx.scale(dpr, dpr);
  }

  render(game: Game): void {
    const ctx = this.ctx;
    ctx.clearRect(0, 0, CW, CH);

    if (game.inventoryOpen) {
      this.drawInventory(game);
      return;
    }

    this.drawHud(game);
    this.drawMinimap(game);

    if (game.gameOver) this.drawOverlay('#c33', 'ИГРА ОКОНЧЕНА');
    else if (game.won) this.drawOverlay('#3c3', 'ПОБЕДА');
  }

  dispose(): void {
    this.ctx.clearRect(0, 0, CW, CH);
  }

  private drawHud(game: Game): void {
    const ctx = this.ctx;
    const p = game.player;
    const room = game.curRoom;

    // Здоровье: сердечки (2 HP = сердце), с фолбэком на полосу.
    const healthBottom = this.drawHealth(p.hp, p.maxHp);

    // Название пресета и номер этажа (для бесконечного спуска).
    ctx.textAlign = 'left'; ctx.fillStyle = '#667'; ctx.font = '11px monospace';
    const floorLabel = game.rules.endless ? ` | Этаж ${game.floor}` : '';
    ctx.fillText(`${game.rules.name}${floorLabel}`, 20, healthBottom + 14);

    // Активные множители стат игрока (от предметов). Показываем только
    // отличные от нейтральных — чтобы не засорять UI в начале забега.
    const s = p.stats;
    const bits: string[] = [];
    if (s.damageMul !== 1) bits.push(`DMG ×${s.damageMul.toFixed(2)}`);
    if (s.fireRateMul !== 1) bits.push(`RATE ×${s.fireRateMul.toFixed(2)}`);
    if (s.rangeMul !== 1) bits.push(`RNG ×${s.rangeMul.toFixed(2)}`);
    if (s.shotSpeedMul !== 1) bits.push(`SPD ×${s.shotSpeedMul.toFixed(2)}`);
    if (bits.length) {
      ctx.fillStyle = '#9a7'; ctx.font = '10px monospace';
      ctx.fillText(bits.join('  '), 20, healthBottom + 28);
    }

    // Индикатор оружия.
    const my = CH - 46;
    const w = p.currentWeapon;
    const wCol = w.type === 'ranged' ? '#4488cc' : '#cc6644';
    ctx.textAlign = 'center';
    ctx.fillStyle = '#0d0d0d'; ctx.fillRect(CW / 2 - 95, my - 18, 190, 34);
    ctx.strokeStyle = wCol; ctx.lineWidth = 2; ctx.strokeRect(CW / 2 - 95, my - 18, 190, 34);
    ctx.fillStyle = wCol; ctx.font = 'bold 17px monospace'; ctx.fillText(`[ ${w.name} ]`, CW / 2, my + 8);
    ctx.fillStyle = '#555'; ctx.font = '11px monospace'; ctx.fillText('[Tab] сменить оружие', CW / 2, my - 26);
    const icon = this.img(w.type === 'ranged' ? 'icon-ranged' : 'icon-melee');
    if (icon) ctx.drawImage(icon, CW / 2 - 90, my - 14, 26, 26);

    // Счётчик врагов / подсказка зачистки.
    ctx.textAlign = 'left';
    const alive = room.enemies.filter((e) => e.alive).length;
    if (alive > 0) {
      ctx.fillStyle = '#aa4444'; ctx.font = '13px monospace';
      ctx.fillText(`▶ ${alive}`, 20, CH - 18);
    } else if (!room.cleared && room.type !== 'spawn') {
      ctx.fillStyle = '#886633'; ctx.font = '13px monospace';
      ctx.fillText('Зачисти комнату', 20, CH - 18);
    }

    // Подпись типа комнаты.
    if (room.visited) {
      const label = { spawn: 'СТАРТ', normal: '', treasure: 'СОКРОВИЩЕ', boss: 'БОСС', secret: 'СЕКРЕТ' }[room.type];
      if (label) {
        ctx.textAlign = 'right'; ctx.fillStyle = '#555'; ctx.font = '11px monospace';
        ctx.fillText(label, CW - 20, OY + RH + 30);
      }
    }
  }

  /** Рисует здоровье сердечками (2 HP = сердце); фолбэк — полоса. Возвращает нижний Y. */
  private drawHealth(hp: number, maxHp: number): number {
    const ctx = this.ctx;
    const x0 = 20, y0 = 16;
    const full = this.img('heart-full');
    const half = this.img('heart-half');
    const empty = this.img('heart-empty');
    if (full && half && empty) {
      const sz = 26, gap = 2;
      const slots = Math.ceil(maxHp / 2);
      for (let i = 0; i < slots; i++) {
        const rem = hp - i * 2;
        const im = rem >= 2 ? full : rem === 1 ? half : empty;
        ctx.drawImage(im, x0 + i * (sz + gap), y0, sz, sz);
      }
      return y0 + sz;
    }
    // Фолбэк — полоса HP.
    const by = 20, bw = 140, bh = 14;
    ctx.fillStyle = '#111'; ctx.fillRect(x0, by, bw, bh);
    ctx.fillStyle = '#2a0a0a'; ctx.fillRect(x0 + 2, by + 2, bw - 4, bh - 4);
    const ratio = Math.max(0, hp / maxHp);
    ctx.fillStyle = ratio > 0.5 ? '#993333' : ratio > 0.25 ? '#994422' : '#663322';
    ctx.fillRect(x0 + 2, by + 2, (bw - 4) * ratio, bh - 4);
    ctx.strokeStyle = '#333'; ctx.lineWidth = 1; ctx.strokeRect(x0, by, bw, bh);
    ctx.fillStyle = '#bbb'; ctx.font = '10px monospace'; ctx.textAlign = 'center';
    ctx.fillText(`HP ${hp}/${maxHp}`, x0 + bw / 2, by + bh - 3);
    return by + bh;
  }

  private drawMinimap(game: Game): void {
    const ctx = this.ctx;
    const ox = CW - 180, oy = 12, cell = 14, gap = 2, cs = cell + gap;

    ctx.fillStyle = 'rgba(0,0,0,0.75)'; ctx.fillRect(ox - 8, oy - 8, cs * 7 + 16, cs * 7 + 16);
    ctx.strokeStyle = '#333'; ctx.lineWidth = 1; ctx.strokeRect(ox - 8, oy - 8, cs * 7 + 16, cs * 7 + 16);

    for (let r = -3; r <= 3; r++) {
      for (let c = -3; c <= 3; c++) {
        const room = game.roomMap.get(game.cc + c, game.cr + r);
        if (!room) continue;
        const x = ox + (c + 3) * cs, y = oy + (r + 3) * cs;

        let color = '#141414';
        if (room.visited) {
          color = { spawn: '#2a5a2a', boss: '#5a1a1a', treasure: '#5a5a1a', secret: '#1a3a5a', normal: '#555' }[room.type];
        }
        ctx.fillStyle = color; ctx.fillRect(x, y, cell, cell);

        if (room.visited) {
          ctx.fillStyle = 'rgba(255,255,255,0.5)';
          if (room.doors.up) ctx.fillRect(x + cs / 2 - 2, y - 2, 4, 3);
          if (room.doors.down) ctx.fillRect(x + cs / 2 - 2, y + cell - 1, 4, 3);
          if (room.doors.left) ctx.fillRect(x - 2, y + cs / 2 - 2, 3, 4);
          if (room.doors.right) ctx.fillRect(x + cell - 1, y + cs / 2 - 2, 3, 4);
        }
        if (c === 0 && r === 0) {
          ctx.strokeStyle = '#ddd'; ctx.lineWidth = 2; ctx.strokeRect(x - 1.5, y - 1.5, cell + 3, cell + 3);
        }
      }
    }
  }

  private drawOverlay(color: string, text: string): void {
    const ctx = this.ctx;
    ctx.fillStyle = 'rgba(0,0,0,0.8)'; ctx.fillRect(0, 0, CW, CH);
    ctx.fillStyle = color; ctx.font = 'bold 56px monospace'; ctx.textAlign = 'center';
    ctx.fillText(text, CW / 2, CH / 2 - 20);
    ctx.fillStyle = '#888'; ctx.font = '18px monospace';
    ctx.fillText('[R] заново', CW / 2, CH / 2 + 40);
    ctx.fillStyle = '#666'; ctx.font = '14px monospace';
    ctx.fillText('[Esc] в меню', CW / 2, CH / 2 + 68);
  }

  private drawInventory(game: import('../core/Game').Game): void {
    const ctx = this.ctx;
    ctx.fillStyle = 'rgba(0,0,0,0.85)'; ctx.fillRect(0, 0, CW, CH);

    ctx.fillStyle = '#ddd'; ctx.font = 'bold 28px monospace'; ctx.textAlign = 'center';
    ctx.fillText('ИНВЕНТАРЬ (2 слота)', CW / 2, 50);

    const p = game.player;
    const iw = 340, ih = 56, gap = 16;
    const total = iw * 2 + gap;
    const ox = CW / 2 - total / 2;
    const oy = 100;

    for (let i = 0; i < 2; i++) {
      const w = p.weapons[i];
      const x = ox + i * (iw + gap);
      const y = oy;
      const selected = i === p.equipped;

      ctx.fillStyle = selected ? '#1a2a1a' : '#111';
      ctx.fillRect(x, y, iw, ih);
      ctx.strokeStyle = selected ? '#4c4' : '#333';
      ctx.lineWidth = selected ? 2 : 1;
      ctx.strokeRect(x, y, iw, ih);

      ctx.fillStyle = '#888'; ctx.font = '14px monospace'; ctx.textAlign = 'left';
      ctx.fillText(`[${i + 1}]`, x + 12, y + 34);

      ctx.fillStyle = selected ? '#4c4' : '#ccc'; ctx.font = 'bold 16px monospace';
      ctx.fillText(w.name, x + 48, y + 34);

      ctx.fillStyle = '#666'; ctx.font = '12px monospace';
      ctx.textAlign = 'right';
      const t = w.type === 'ranged' ? 'ДАЛЬНИЙ' : 'БЛИЖНИЙ';
      ctx.fillText(`${t}  DMG:${w.damage}  CD:${w.cooldown}`, x + iw - 12, y + 34);

      const icon = this.img(w.type === 'ranged' ? 'icon-ranged' : 'icon-melee');
      if (icon) ctx.drawImage(icon, x + iw - 44, y + 16, 24, 24);
    }

    ctx.fillStyle = '#555'; ctx.font = '13px monospace'; ctx.textAlign = 'center';
    ctx.fillText('[E] закрыть  |  [1] [2] слот  |  [Tab] переключить', CW / 2, CH - 30);
  }
}
