import { createCanvas, clear, getCanvasCoords } from '../../../core/canvas.js';
import { getAsset, getColor } from '../../../core/theme.js';

function LIGHT_SQ() { return getColor('board_light', '#F0D9B5'); }
function DARK_SQ() { return getColor('board_dark', '#B58863'); }
function HIGHLIGHT_LIGHT() { return getColor('board_highlight_light', '#CDD16A'); }
function HIGHLIGHT_DARK() { return getColor('board_highlight_dark', '#AAA23A'); }
function CHECK_COLOR() { return (getColor('board_check', '#FF6B6B') || '#FF6B6B') + '88'; }
const MOVE_DOT = 'rgba(0,0,0,0.25)';
const CAPTURE_RING = 'rgba(0,0,0,0.25)';

const PIECE_ASSET_MAP = {
  K: 'chess_piece_wK', Q: 'chess_piece_wQ', R: 'chess_piece_wR',
  B: 'chess_piece_wB', N: 'chess_piece_wN', P: 'chess_piece_wP',
  k: 'chess_piece_bK', q: 'chess_piece_bQ', r: 'chess_piece_bR',
  b: 'chess_piece_bB', n: 'chess_piece_bN', p: 'chess_piece_bP',
};

const pieceImages = {};
let pieceImagesLoaded = false;
let boardInstance = null;

function loadPieceImages() {
  if (pieceImagesLoaded) return;
  let anyFound = false;
  let pending = 0;
  for (const [piece, slot] of Object.entries(PIECE_ASSET_MAP)) {
    const url = getAsset(slot);
    if (url) {
      anyFound = true;
      if (pieceImages[piece]?.src === url) continue;
      const img = new Image();
      pending++;
      img.onload = () => { if (--pending === 0 && boardInstance) boardInstance.render(); };
      img.onerror = () => { --pending; };
      img.src = url;
      pieceImages[piece] = img;
    }
  }
  if (anyFound) pieceImagesLoaded = true;
}

const PIECE_PATHS = {
  K: (ctx, x, y, s) => { drawPiece(ctx, x, y, s, '#fff', '#000', kingPath); },
  Q: (ctx, x, y, s) => { drawPiece(ctx, x, y, s, '#fff', '#000', queenPath); },
  R: (ctx, x, y, s) => { drawPiece(ctx, x, y, s, '#fff', '#000', rookPath); },
  B: (ctx, x, y, s) => { drawPiece(ctx, x, y, s, '#fff', '#000', bishopPath); },
  N: (ctx, x, y, s) => { drawPiece(ctx, x, y, s, '#fff', '#000', knightPath); },
  P: (ctx, x, y, s) => { drawPiece(ctx, x, y, s, '#fff', '#000', pawnPath); },
  k: (ctx, x, y, s) => { drawPiece(ctx, x, y, s, '#333', '#000', kingPath); },
  q: (ctx, x, y, s) => { drawPiece(ctx, x, y, s, '#333', '#000', queenPath); },
  r: (ctx, x, y, s) => { drawPiece(ctx, x, y, s, '#333', '#000', rookPath); },
  b: (ctx, x, y, s) => { drawPiece(ctx, x, y, s, '#333', '#000', bishopPath); },
  n: (ctx, x, y, s) => { drawPiece(ctx, x, y, s, '#333', '#000', knightPath); },
  p: (ctx, x, y, s) => { drawPiece(ctx, x, y, s, '#333', '#000', pawnPath); },
};

function drawPiece(ctx, x, y, s, fill, stroke, pathFn) {
  ctx.save();
  ctx.translate(x, y);
  ctx.scale(s / 45, s / 45);
  ctx.fillStyle = fill;
  ctx.strokeStyle = stroke;
  ctx.lineWidth = 1.5;
  pathFn(ctx);
  ctx.restore();
}

function kingPath(ctx) {
  ctx.beginPath();
  ctx.moveTo(22.5, 11.63);
  ctx.lineTo(22.5, 6);
  ctx.moveTo(20, 8);
  ctx.lineTo(25, 8);
  ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(22.5, 25);
  ctx.bezierCurveTo(22.5, 25, 27, 17.5, 25.5, 14.5);
  ctx.bezierCurveTo(25.5, 14.5, 24.5, 12, 22.5, 12);
  ctx.bezierCurveTo(20.5, 12, 19.5, 14.5, 19.5, 14.5);
  ctx.bezierCurveTo(18, 17.5, 22.5, 25, 22.5, 25);
  ctx.fill();
  ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(12.5, 37);
  ctx.bezierCurveTo(18, 40.5, 27, 40.5, 32.5, 37);
  ctx.lineTo(32.5, 30);
  ctx.bezierCurveTo(32.5, 30, 41.5, 25.5, 38.5, 19.5);
  ctx.bezierCurveTo(34.5, 13, 25, 16, 22.5, 23.5);
  ctx.lineTo(22.5, 27);
  ctx.lineTo(22.5, 23.5);
  ctx.bezierCurveTo(20, 16, 10.5, 13, 6.5, 19.5);
  ctx.bezierCurveTo(3.5, 25.5, 12.5, 30, 12.5, 30);
  ctx.lineTo(12.5, 37);
  ctx.fill();
  ctx.stroke();
}

function queenPath(ctx) {
  ctx.beginPath();
  ctx.arc(6, 12, 2.5, 0, Math.PI * 2);
  ctx.arc(14, 9, 2.5, 0, Math.PI * 2);
  ctx.arc(22.5, 8, 2.5, 0, Math.PI * 2);
  ctx.arc(31, 9, 2.5, 0, Math.PI * 2);
  ctx.arc(39, 12, 2.5, 0, Math.PI * 2);
  ctx.fill();
  ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(9, 26);
  ctx.bezierCurveTo(17.5, 24.5, 30, 24.5, 36, 26);
  ctx.lineTo(38.5, 13.5);
  ctx.lineTo(31, 25);
  ctx.lineTo(30.7, 10.9);
  ctx.lineTo(25.5, 24.5);
  ctx.lineTo(22.5, 10);
  ctx.lineTo(19.5, 24.5);
  ctx.lineTo(14.3, 10.9);
  ctx.lineTo(14, 25);
  ctx.lineTo(6.5, 13.5);
  ctx.lineTo(9, 26);
  ctx.closePath();
  ctx.fill();
  ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(9, 26);
  ctx.bezierCurveTo(9, 28, 10.5, 31.5, 12.5, 33);
  ctx.bezierCurveTo(16, 35.5, 23, 36, 23, 36);
  ctx.bezierCurveTo(23, 36, 30, 35.5, 33.5, 33);
  ctx.bezierCurveTo(35.5, 31.5, 36, 28, 36, 26);
  ctx.bezierCurveTo(28, 24.5, 17.5, 24.5, 9, 26);
  ctx.fill();
  ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(12.5, 37);
  ctx.bezierCurveTo(18, 40.5, 27, 40.5, 32.5, 37);
  ctx.lineTo(32.5, 33.5);
  ctx.bezierCurveTo(27, 36, 18, 36, 12.5, 33.5);
  ctx.lineTo(12.5, 37);
  ctx.fill();
  ctx.stroke();
}

function rookPath(ctx) {
  ctx.beginPath();
  ctx.moveTo(9, 39);
  ctx.lineTo(36, 39);
  ctx.lineTo(36, 36);
  ctx.lineTo(9, 36);
  ctx.lineTo(9, 39);
  ctx.fill(); ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(12, 36);
  ctx.lineTo(12, 32);
  ctx.lineTo(33, 32);
  ctx.lineTo(33, 36);
  ctx.lineTo(12, 36);
  ctx.fill(); ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(11, 14);
  ctx.lineTo(11, 9);
  ctx.lineTo(15, 9);
  ctx.lineTo(15, 11);
  ctx.lineTo(20, 11);
  ctx.lineTo(20, 9);
  ctx.lineTo(25, 9);
  ctx.lineTo(25, 11);
  ctx.lineTo(30, 11);
  ctx.lineTo(30, 9);
  ctx.lineTo(34, 9);
  ctx.lineTo(34, 14);
  ctx.fill(); ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(14, 32);
  ctx.lineTo(11, 14);
  ctx.lineTo(34, 14);
  ctx.lineTo(31, 32);
  ctx.lineTo(14, 32);
  ctx.fill(); ctx.stroke();
}

function bishopPath(ctx) {
  ctx.beginPath();
  ctx.moveTo(9, 36);
  ctx.bezierCurveTo(12.4, 35.4, 19.5, 34.1, 22.5, 34);
  ctx.bezierCurveTo(25.5, 34.1, 32.6, 35.4, 36, 36);
  ctx.bezierCurveTo(36, 36, 37.7, 36.8, 39, 38);
  ctx.bezierCurveTo(38.1, 40.5, 14, 40.5, 6, 38);
  ctx.bezierCurveTo(7.3, 36.8, 9, 36, 9, 36);
  ctx.fill(); ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(15, 32);
  ctx.bezierCurveTo(17.5, 34.5, 27.5, 34.5, 30, 32);
  ctx.bezierCurveTo(30.5, 30.5, 30, 30, 30, 30);
  ctx.bezierCurveTo(30, 27.5, 27.5, 26, 27.5, 26);
  ctx.bezierCurveTo(33, 24.5, 33.5, 14.5, 22.5, 10.5);
  ctx.bezierCurveTo(11.5, 14.5, 12, 24.5, 17.5, 26);
  ctx.bezierCurveTo(17.5, 26, 15, 27.5, 15, 30);
  ctx.bezierCurveTo(15, 30, 14.5, 30.5, 15, 32);
  ctx.fill(); ctx.stroke();
  ctx.beginPath();
  ctx.arc(22.5, 8, 2.5, 0, Math.PI * 2);
  ctx.fill(); ctx.stroke();
}

function knightPath(ctx) {
  ctx.beginPath();
  ctx.moveTo(22, 10);
  ctx.bezierCurveTo(32.5, 11, 38.5, 18, 38, 39);
  ctx.lineTo(15, 39);
  ctx.bezierCurveTo(15, 30, 25, 32.5, 23, 18);
  ctx.fill(); ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(24, 18);
  ctx.bezierCurveTo(24.38, 20.91, 18.45, 25.37, 16, 27);
  ctx.bezierCurveTo(13, 29, 13.18, 31.34, 11, 31);
  ctx.bezierCurveTo(9.96, 30.06, 12.41, 27.96, 11, 28);
  ctx.bezierCurveTo(10, 28, 11.19, 29.23, 10, 30);
  ctx.bezierCurveTo(9, 30, 5.97, 31.34, 8, 28);
  ctx.bezierCurveTo(10.91, 23.67, 12.37, 19.87, 11, 16);
  ctx.bezierCurveTo(10, 13, 13, 7.5, 18, 7);
  ctx.bezierCurveTo(18, 7, 20, 7.5, 22, 10);
  ctx.fill(); ctx.stroke();
}

function pawnPath(ctx) {
  ctx.beginPath();
  ctx.arc(22.5, 12, 4, 0, Math.PI * 2);
  ctx.fill(); ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(15.5, 26);
  ctx.bezierCurveTo(17, 22.5, 20, 19.5, 22.5, 18);
  ctx.bezierCurveTo(25, 19.5, 28, 22.5, 29.5, 26);
  ctx.bezierCurveTo(30.5, 28, 28.3, 31, 22.5, 31);
  ctx.bezierCurveTo(16.7, 31, 14.5, 28, 15.5, 26);
  ctx.fill(); ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(10, 39);
  ctx.bezierCurveTo(10, 36, 14, 33, 22.5, 33);
  ctx.bezierCurveTo(31, 33, 35, 36, 35, 39);
  ctx.lineTo(10, 39);
  ctx.fill(); ctx.stroke();
}

export class ChessBoard {
  constructor(container, options = {}) {
    this.flipped = options.flipped || false;
    this.squareSize = 0;
    this.pieces = {};
    this.highlights = { lastMove: null, selected: null, legalMoves: [], check: null };
    this.dragging = null;
    this.animating = null;
    this.onMove = options.onMove || null;
    this.interactive = options.interactive !== false;

    this.container = container;
    this.wrapper = document.createElement('div');
    this.wrapper.style.cssText = 'position:relative;width:100%;max-width:500px;margin:0 auto;aspect-ratio:1;';
    container.appendChild(this.wrapper);

    boardInstance = this;
    this.setupCanvas();
    this.bindEvents();
  }

  setupCanvas() {
    const containerH = this.container.clientHeight || 500;
    const containerW = this.container.clientWidth || this.wrapper.clientWidth || 360;
    const size = Math.min(containerW - 4, containerH - 4, 500);
    this.squareSize = size / 8;
    this.wrapper.style.maxWidth = size + 'px';
    const { canvas, ctx } = createCanvas(this.wrapper, size, size);
    this.canvas = canvas;
    this.ctx = ctx;
    this.size = size;
    this.canvas.style.width = size + 'px';
    this.canvas.style.height = size + 'px';
    this.canvas.style.display = 'block';
    this.canvas.style.borderRadius = '4px';
    this.canvas.style.boxShadow = '0 4px 20px rgba(0,0,0,0.5)';
    this.canvas.style.touchAction = 'none';
  }

  bindEvents() {
    if (!this.interactive) return;
    this.canvas.addEventListener('pointerdown', (e) => this.onPointerDown(e));
    this.canvas.addEventListener('pointermove', (e) => this.onPointerMove(e));
    this.canvas.addEventListener('pointerup', (e) => this.onPointerUp(e));
  }

  setPosition(fenStr) {
    this.pieces = {};
    const fenBoard = fenStr.split(' ')[0];
    const rows = fenBoard.split('/');
    for (let r = 0; r < 8; r++) {
      let col = 0;
      for (const ch of rows[r]) {
        if (ch >= '1' && ch <= '8') { col += parseInt(ch); }
        else {
          const file = String.fromCharCode(97 + col);
          const rank = String(8 - r);
          this.pieces[file + rank] = ch;
          col++;
        }
      }
    }
    this.draw();
  }

  draw() {
    const ctx = this.ctx;
    const sq = this.squareSize;
    clear(ctx, this.size, this.size);

    // Draw squares
    for (let r = 0; r < 8; r++) {
      for (let c = 0; c < 8; c++) {
        const x = c * sq;
        const y = r * sq;
        const isLight = (r + c) % 2 === 0;

        const file = this.flipped ? 7 - c : c;
        const rank = this.flipped ? r : 7 - r;
        const square = String.fromCharCode(97 + file) + String(rank + 1);

        let color = isLight ? LIGHT_SQ() : DARK_SQ();
        if (this.highlights.lastMove && (square === this.highlights.lastMove.from || square === this.highlights.lastMove.to)) {
          color = isLight ? HIGHLIGHT_LIGHT() : HIGHLIGHT_DARK();
        }
        if (this.highlights.selected === square) {
          color = isLight ? HIGHLIGHT_LIGHT() : HIGHLIGHT_DARK();
        }

        ctx.fillStyle = color;
        ctx.fillRect(x, y, sq, sq);

        if (this.highlights.check === square) {
          ctx.fillStyle = CHECK_COLOR();
          ctx.fillRect(x, y, sq, sq);
        }
      }
    }

    // Draw legal move indicators
    for (const move of this.highlights.legalMoves) {
      const { x, y } = this.squareToXY(move.to);
      if (this.pieces[move.to]) {
        ctx.beginPath();
        ctx.arc(x + sq / 2, y + sq / 2, sq * 0.45, 0, Math.PI * 2);
        ctx.lineWidth = sq * 0.08;
        ctx.strokeStyle = CAPTURE_RING;
        ctx.stroke();
      } else {
        ctx.beginPath();
        ctx.arc(x + sq / 2, y + sq / 2, sq * 0.15, 0, Math.PI * 2);
        ctx.fillStyle = MOVE_DOT;
        ctx.fill();
      }
    }

    // Draw pieces
    loadPieceImages();
    const pad = sq * 0.1;
    for (const [square, piece] of Object.entries(this.pieces)) {
      if (this.dragging && this.dragging.square === square) continue;
      if (this.animating && this.animating.square === square) continue;
      const { x, y } = this.squareToXY(square);
      this.drawPieceAt(ctx, piece, x + pad, y + pad, sq - pad * 2);
    }

    // Draw animating piece with squish
    if (this.animating) {
      const { piece, x, y, scale } = this.animating;
      const sc = scale || 1;
      const s = (sq - pad * 2) * sc;
      const offset = ((sq - pad * 2) - s) / 2;
      this.drawPieceAt(ctx, piece, x + pad + offset, y + pad + offset, s);
    }

    // Draw dragging piece
    if (this.dragging) {
      const ds = sq * 1.1;
      this.drawPieceAt(ctx, this.dragging.piece, this.dragging.x - ds / 2, this.dragging.y - ds / 2, ds);
    }

    // Coordinates
    ctx.font = `bold ${sq * 0.22}px sans-serif`;
    for (let i = 0; i < 8; i++) {
      const file = this.flipped ? 7 - i : i;
      const rank = this.flipped ? i + 1 : 8 - i;
      const isLight = (7 + i) % 2 === 0;
      ctx.fillStyle = isLight ? DARK_SQ() : LIGHT_SQ();
      ctx.textAlign = 'left';
      ctx.textBaseline = 'bottom';
      ctx.fillText(String.fromCharCode(97 + file), i * sq + 2, this.size - 2);
      ctx.textAlign = 'right';
      ctx.textBaseline = 'top';
      ctx.fillText(String(rank), this.size - 2, i * sq + 2);
    }
  }

  squareToXY(square) {
    const file = square.charCodeAt(0) - 97;
    const rank = parseInt(square[1]) - 1;
    const c = this.flipped ? 7 - file : file;
    const r = this.flipped ? rank : 7 - rank;
    return { x: c * this.squareSize, y: r * this.squareSize };
  }

  drawPieceAt(ctx, piece, x, y, s) {
    const img = pieceImages[piece];
    if (img && img.complete && img.naturalWidth > 0) {
      const iw = img.naturalWidth;
      const ih = img.naturalHeight;
      const scale = Math.min(s / iw, s / ih);
      const dw = iw * scale;
      const dh = ih * scale;
      ctx.drawImage(img, x + (s - dw) / 2, y + (s - dh) / 2, dw, dh);
    } else if (PIECE_PATHS[piece]) {
      PIECE_PATHS[piece](ctx, x, y, s);
    }
  }

  xyToSquare(x, y) {
    const c = Math.floor(x / this.squareSize);
    const r = Math.floor(y / this.squareSize);
    const file = this.flipped ? 7 - c : c;
    const rank = this.flipped ? r : 7 - r;
    if (file < 0 || file > 7 || rank < 0 || rank > 7) return null;
    return String.fromCharCode(97 + file) + String(rank + 1);
  }

  onPointerDown(e) {
    if (!this.interactive) return;
    const { x, y } = getCanvasCoords(this.canvas, e);
    const square = this.xyToSquare(x, y);
    if (!square) return;

    const piece = this.pieces[square];
    if (piece && this.canSelect(piece)) {
      this.dragging = { square, piece, x, y };
      this.highlights.selected = square;
      this.showLegalMoves(square);
      this.canvas.setPointerCapture(e.pointerId);
      this.draw();
    } else if (this.highlights.selected) {
      this.tryMove(this.highlights.selected, square);
    }
  }

  onPointerMove(e) {
    if (!this.dragging) return;
    const { x, y } = getCanvasCoords(this.canvas, e);
    this.dragging.x = x;
    this.dragging.y = y;
    this.draw();
  }

  onPointerUp(e) {
    if (!this.dragging) return;
    const { x, y } = getCanvasCoords(this.canvas, e);
    const square = this.xyToSquare(x, y);
    const from = this.dragging.square;
    this.dragging = null;

    if (square && square !== from) {
      this.tryMove(from, square);
    } else {
      this.draw();
    }
  }

  tryMove(from, to) {
    const isLegal = this.highlights.legalMoves.some(m => m.from === from && m.to === to);
    if (isLegal && this.onMove) {
      this.onMove(from, to);
    }
    this.highlights.selected = null;
    this.highlights.legalMoves = [];
    this.draw();
  }

  canSelect(piece) { return true; }
  showLegalMoves(square) {}

  setLastMove(from, to) {
    this.highlights.lastMove = { from, to };
    this.draw();
  }

  setCheck(square) {
    this.highlights.check = square;
    this.draw();
  }

  clearHighlights() {
    this.highlights = { lastMove: null, selected: null, legalMoves: [], check: null };
    this.draw();
  }

  flip() {
    this.flipped = !this.flipped;
    this.draw();
  }

  resize() {
    const size = Math.min(this.wrapper.clientWidth || 360, 400);
    this.squareSize = size / 8;
    this.size = size;
    const dpr = window.devicePixelRatio || 1;
    this.canvas.width = size * dpr;
    this.canvas.height = size * dpr;
    this.ctx.scale(dpr, dpr);
    this.draw();
  }

  animateMove(from, to, callback) {
    const piece = this.pieces[from];
    if (!piece) { callback?.(); return; }

    const fromXY = this.squareToXY(from);
    const toXY = this.squareToXY(to);
    const isCapture = !!this.pieces[to];
    const duration = 120;
    const startTime = performance.now();
    const squishAmount = isCapture ? 0.72 : 0.85;

    this.animating = { square: from, piece, x: fromXY.x, y: fromXY.y, scale: 1 };

    const step = (now) => {
      const elapsed = now - startTime;
      const t = Math.min(elapsed / duration, 1);
      const ease = 1 - Math.pow(1 - t, 3);

      this.animating.x = fromXY.x + (toXY.x - fromXY.x) * ease;
      this.animating.y = fromXY.y + (toXY.y - fromXY.y) * ease;

      // Squish: scale dips at midpoint then pops back
      const squishT = Math.sin(t * Math.PI);
      this.animating.scale = 1 - (1 - squishAmount) * squishT;

      this.draw();

      if (t < 1) {
        requestAnimationFrame(step);
      } else {
        this.animating = null;
        callback?.();
      }
    };

    requestAnimationFrame(step);
  }

  destroy() {
    this.wrapper.remove();
  }
}
