/**
 * MapManager.js
 * Manages the tile-based game map: loading, rendering, terrain state, and collision queries.
 */

const {
  GRID_COLS,
  GRID_ROWS,
  TILE_SIZE,
  MAP_OFFSET_X,
  MAP_OFFSET_Y,
  TERRAIN,
  COLORS,
} = require('../base/GameGlobal');

class MapManager {
  constructor() {
    /** @type {number[][]} 2D grid of terrain types */
    this._grid = [];
    /** @type {boolean} Whether the base has been destroyed */
    this._baseDestroyed = false;
    /** @type {boolean} Whether base walls are temporarily steel */
    this._baseSteelTimer = 0;
    /** @type {number[][]} Backup of original base wall positions */
    this._baseWallPositions = [];
    /** @type {Object} HP map for base wall tiles, keyed by 'row,col' */
    this._baseWallHP = {};
    /** Base wall default HP (hits required to destroy) */
    this.BASE_WALL_MAX_HP = 3;
  }

  /**
   * Load a level grid.
   * @param {number[][]} grid - GRID_ROWS × GRID_COLS array of terrain values.
   */
  loadGrid(grid) {
    // Deep clone so we don't mutate level data
    this._grid = grid.map((row) => [...row]);
    this._baseDestroyed = false;
    this._baseSteelTimer = 0;

    // Record base wall positions for shovel power-up and initialize HP
    this._baseWallPositions = [];
    this._baseWallHP = {};
    for (let r = 0; r < GRID_ROWS; r++) {
      for (let c = 0; c < GRID_COLS; c++) {
        if (this._grid[r][c] === TERRAIN.BASE_WALL) {
          this._baseWallPositions.push([r, c]);
          this._baseWallHP[`${r},${c}`] = this.BASE_WALL_MAX_HP;
        }
      }
    }
  }

  /**
   * Update map state (e.g., shovel timer).
   * @param {number} dt - Delta time in seconds.
   */
  update(dt) {
    if (this._baseSteelTimer > 0) {
      this._baseSteelTimer -= dt * 1000;
      if (this._baseSteelTimer <= 0) {
        this._baseSteelTimer = 0;
        // Revert steel walls back to brick and restore HP
        for (const [r, c] of this._baseWallPositions) {
          if (this._grid[r][c] === TERRAIN.STEEL) {
            this._grid[r][c] = TERRAIN.BASE_WALL;
            this._baseWallHP[`${r},${c}`] = this.BASE_WALL_MAX_HP;
          }
        }
      }
    }
  }

  /**
   * Render the map tiles.
   * @param {CanvasRenderingContext2D} ctx
   */
  render(ctx) {
    for (let r = 0; r < GRID_ROWS; r++) {
      for (let c = 0; c < GRID_COLS; c++) {
        const terrain = this._grid[r][c];
        if (terrain === TERRAIN.EMPTY) continue;

        const x = MAP_OFFSET_X + c * TILE_SIZE;
        const y = MAP_OFFSET_Y + r * TILE_SIZE;

        switch (terrain) {
      case TERRAIN.BRICK:
        this._drawBrick(ctx, x, y);
        break;
      case TERRAIN.BASE_WALL:
        this._drawBaseWall(ctx, x, y, r, c);
        break;
          case TERRAIN.STEEL:
            this._drawSteel(ctx, x, y);
            break;
          case TERRAIN.RIVER:
            this._drawRiver(ctx, x, y);
            break;
          case TERRAIN.FOREST:
            // Forest is drawn in a separate pass (on top of tanks)
            break;
          case TERRAIN.BASE:
            this._drawBase(ctx, x, y);
            break;
        }
      }
    }
  }

  /**
   * Render the forest overlay (drawn after tanks so it covers them).
   * @param {CanvasRenderingContext2D} ctx
   */
  renderForestOverlay(ctx) {
    for (let r = 0; r < GRID_ROWS; r++) {
      for (let c = 0; c < GRID_COLS; c++) {
        if (this._grid[r][c] === TERRAIN.FOREST) {
          const x = MAP_OFFSET_X + c * TILE_SIZE;
          const y = MAP_OFFSET_Y + r * TILE_SIZE;
          this._drawForest(ctx, x, y);
        }
      }
    }
  }

  // ============================================================
  // Tile Drawing Methods
  // ============================================================

  _drawBrick(ctx, x, y) {
    const s = TILE_SIZE;
    ctx.fillStyle = COLORS.BRICK;
    ctx.fillRect(x, y, s, s);

    // Brick pattern (mortar lines)
    ctx.strokeStyle = '#8B4513';
    ctx.lineWidth = 1;
    // Horizontal line
    ctx.beginPath();
    ctx.moveTo(x, y + s / 2);
    ctx.lineTo(x + s, y + s / 2);
    ctx.stroke();
    // Vertical lines (offset pattern)
    ctx.beginPath();
    ctx.moveTo(x + s / 2, y);
    ctx.lineTo(x + s / 2, y + s / 2);
    ctx.stroke();
    ctx.beginPath();
    ctx.moveTo(x + s / 4, y + s / 2);
    ctx.lineTo(x + s / 4, y + s);
    ctx.stroke();
    ctx.beginPath();
    ctx.moveTo(x + s * 3 / 4, y + s / 2);
    ctx.lineTo(x + s * 3 / 4, y + s);
    ctx.stroke();
  }

  _drawSteel(ctx, x, y) {
    const s = TILE_SIZE;
    ctx.fillStyle = COLORS.STEEL;
    ctx.fillRect(x, y, s, s);

    // Steel shine effect
    ctx.fillStyle = '#A0A0A0';
    ctx.fillRect(x + 2, y + 2, s / 2 - 2, s / 2 - 2);
    ctx.fillRect(x + s / 2 + 1, y + s / 2 + 1, s / 2 - 3, s / 2 - 3);

    // Border
    ctx.strokeStyle = '#606060';
    ctx.lineWidth = 1;
    ctx.strokeRect(x + 0.5, y + 0.5, s - 1, s - 1);
  }

  _drawRiver(ctx, x, y) {
    const s = TILE_SIZE;
    ctx.fillStyle = COLORS.RIVER;
    ctx.fillRect(x, y, s, s);

    // Wave pattern
    ctx.strokeStyle = '#5B9BD5';
    ctx.lineWidth = 1;
    for (let i = 0; i < 3; i++) {
      const wy = y + s * (i + 1) / 4;
      ctx.beginPath();
      ctx.moveTo(x, wy);
      ctx.quadraticCurveTo(x + s / 4, wy - 2, x + s / 2, wy);
      ctx.quadraticCurveTo(x + s * 3 / 4, wy + 2, x + s, wy);
      ctx.stroke();
    }
  }

  _drawForest(ctx, x, y) {
    const s = TILE_SIZE;
    ctx.fillStyle = COLORS.FOREST;
    ctx.globalAlpha = 0.85;
    ctx.fillRect(x, y, s, s);

    // Tree pattern
    ctx.fillStyle = '#008000';
    const r = s / 4;
    ctx.beginPath();
    ctx.arc(x + s / 3, y + s / 3, r, 0, Math.PI * 2);
    ctx.fill();
    ctx.beginPath();
    ctx.arc(x + s * 2 / 3, y + s / 2, r, 0, Math.PI * 2);
    ctx.fill();
    ctx.beginPath();
    ctx.arc(x + s / 2, y + s * 2 / 3, r, 0, Math.PI * 2);
    ctx.fill();

    ctx.globalAlpha = 1;
  }

  /**
   * Draw a base wall tile with visual HP indicator.
   * @private
   */
  _drawBaseWall(ctx, x, y, row, col) {
    const s = TILE_SIZE;
    const key = `${row},${col}`;
    const hp = this._baseWallHP[key] || 0;
    const maxHP = this.BASE_WALL_MAX_HP;
    const ratio = hp / maxHP;

    // Base color darkens as HP decreases
    if (ratio > 0.66) {
      ctx.fillStyle = '#C47832'; // full HP - bright brick
    } else if (ratio > 0.33) {
      ctx.fillStyle = '#A05A20'; // medium HP - darker
    } else {
      ctx.fillStyle = '#7A3E10'; // low HP - very dark
    }
    ctx.fillRect(x, y, s, s);

    // Brick pattern (mortar lines)
    ctx.strokeStyle = '#5A2D0C';
    ctx.lineWidth = 1;
    ctx.beginPath();
    ctx.moveTo(x, y + s / 2);
    ctx.lineTo(x + s, y + s / 2);
    ctx.stroke();
    ctx.beginPath();
    ctx.moveTo(x + s / 2, y);
    ctx.lineTo(x + s / 2, y + s / 2);
    ctx.stroke();
    ctx.beginPath();
    ctx.moveTo(x + s / 4, y + s / 2);
    ctx.lineTo(x + s / 4, y + s);
    ctx.stroke();
    ctx.beginPath();
    ctx.moveTo(x + s * 3 / 4, y + s / 2);
    ctx.lineTo(x + s * 3 / 4, y + s);
    ctx.stroke();

    // Reinforcement border to distinguish from normal brick
    ctx.strokeStyle = '#FFD700';
    ctx.lineWidth = 1.5;
    ctx.strokeRect(x + 1, y + 1, s - 2, s - 2);

    // HP indicator dots at top
    const dotR = 2;
    const dotSpacing = 8;
    const startX = x + s / 2 - ((maxHP - 1) * dotSpacing) / 2;
    for (let i = 0; i < maxHP; i++) {
      ctx.fillStyle = i < hp ? '#FFD700' : '#333333';
      ctx.beginPath();
      ctx.arc(startX + i * dotSpacing, y + 5, dotR, 0, Math.PI * 2);
      ctx.fill();
    }
  }

  _drawBase(ctx, x, y) {
    const s = TILE_SIZE;
    if (this._baseDestroyed) {
      // Destroyed base
      ctx.fillStyle = '#333333';
      ctx.fillRect(x, y, s, s);
      ctx.strokeStyle = '#FF0000';
      ctx.lineWidth = 2;
      ctx.beginPath();
      ctx.moveTo(x + 2, y + 2);
      ctx.lineTo(x + s - 2, y + s - 2);
      ctx.moveTo(x + s - 2, y + 2);
      ctx.lineTo(x + 2, y + s - 2);
      ctx.stroke();
    } else {
      // Eagle / base icon
      ctx.fillStyle = COLORS.BASE;
      ctx.fillRect(x, y, s, s);

      // Simple eagle shape
      ctx.fillStyle = '#000000';
      ctx.font = `${Math.floor(s * 0.7)}px Arial`;
      ctx.textAlign = 'center';
      ctx.textBaseline = 'middle';
      ctx.fillText('🦅', x + s / 2, y + s / 2);
    }
  }

  // ============================================================
  // Collision & Query Methods
  // ============================================================

  /**
   * Get terrain type at a grid position.
   * @param {number} row
   * @param {number} col
   * @returns {number} Terrain type, or -1 if out of bounds.
   */
  getTerrain(row, col) {
    if (row < 0 || row >= GRID_ROWS || col < 0 || col >= GRID_COLS) return -1;
    return this._grid[row][col];
  }

  /**
   * Set terrain at a grid position.
   * @param {number} row
   * @param {number} col
   * @param {number} terrain
   */
  setTerrain(row, col, terrain) {
    if (row < 0 || row >= GRID_ROWS || col < 0 || col >= GRID_COLS) return;
    this._grid[row][col] = terrain;
  }

  /**
   * Convert pixel coordinates to grid coordinates.
   * @param {number} px - Pixel X (screen space).
   * @param {number} py - Pixel Y (screen space).
   * @returns {{row: number, col: number}}
   */
  pixelToGrid(px, py) {
    const col = Math.floor((px - MAP_OFFSET_X) / TILE_SIZE);
    const row = Math.floor((py - MAP_OFFSET_Y) / TILE_SIZE);
    return { row, col };
  }

  /**
   * Convert grid coordinates to pixel coordinates (top-left of tile).
   * @param {number} row
   * @param {number} col
   * @returns {{x: number, y: number}}
   */
  gridToPixel(row, col) {
    return {
      x: MAP_OFFSET_X + col * TILE_SIZE,
      y: MAP_OFFSET_Y + row * TILE_SIZE,
    };
  }

  /**
   * Check if a terrain tile blocks tank movement.
   * @param {number} row
   * @param {number} col
   * @returns {boolean}
   */
  isTankBlocking(row, col) {
    const t = this.getTerrain(row, col);
    if (t === -1) return true; // out of bounds
    return (
      t === TERRAIN.BRICK ||
      t === TERRAIN.STEEL ||
      t === TERRAIN.RIVER ||
      t === TERRAIN.BASE ||
      t === TERRAIN.BASE_WALL
    );
  }

  /**
   * Check if a terrain tile blocks bullets.
   * @param {number} row
   * @param {number} col
   * @param {boolean} canBreakSteel - Whether the bullet can break steel.
   * @returns {'block'|'destroy'|'pass'} Result of bullet hitting this tile.
   */
  bulletHitTerrain(row, col, canBreakSteel) {
    const t = this.getTerrain(row, col);
    if (t === -1) return 'block'; // out of bounds = wall

    switch (t) {
      case TERRAIN.BRICK:
        // Destroy the brick
        this._grid[row][col] = TERRAIN.EMPTY;
        return 'destroy';

      case TERRAIN.BASE_WALL: {
        // Base wall has HP, reduce it
        const key = `${row},${col}`;
        const currentHP = (this._baseWallHP[key] || 1) - 1;
        this._baseWallHP[key] = currentHP;
        if (currentHP <= 0) {
          this._grid[row][col] = TERRAIN.EMPTY;
          return 'destroy';
        }
        return 'block'; // damaged but not destroyed
      }

      case TERRAIN.STEEL:
        if (canBreakSteel) {
          this._grid[row][col] = TERRAIN.EMPTY;
          return 'destroy';
        }
        return 'block';

      case TERRAIN.BASE:
        this._baseDestroyed = true;
        return 'destroy';

      case TERRAIN.RIVER:
        return 'pass'; // bullets fly over river

      case TERRAIN.FOREST:
        return 'pass'; // bullets pass through forest

      default:
        return 'pass';
    }
  }

  /**
   * Check if a rectangular area collides with any blocking terrain.
   * Used for tank movement collision.
   * @param {number} x - Left edge (pixel).
   * @param {number} y - Top edge (pixel).
   * @param {number} w - Width.
   * @param {number} h - Height.
   * @returns {boolean} True if any blocking tile overlaps.
   */
  rectCollidesWithTerrain(x, y, w, h) {
    // Get grid range that the rect covers
    const startCol = Math.floor((x - MAP_OFFSET_X) / TILE_SIZE);
    const endCol = Math.floor((x + w - 1 - MAP_OFFSET_X) / TILE_SIZE);
    const startRow = Math.floor((y - MAP_OFFSET_Y) / TILE_SIZE);
    const endRow = Math.floor((y + h - 1 - MAP_OFFSET_Y) / TILE_SIZE);

    for (let r = startRow; r <= endRow; r++) {
      for (let c = startCol; c <= endCol; c++) {
        if (this.isTankBlocking(r, c)) {
          return true;
        }
      }
    }
    return false;
  }

  /**
   * Activate shovel power-up: convert base walls to steel temporarily.
   * @param {number} duration - Duration in ms.
   */
  activateShovel(duration) {
    this._baseSteelTimer = duration;
    for (const [r, c] of this._baseWallPositions) {
      if (this._grid[r][c] === TERRAIN.BASE_WALL || this._grid[r][c] === TERRAIN.EMPTY) {
        this._grid[r][c] = TERRAIN.STEEL;
      }
    }
  }

  /** Whether the base has been destroyed. */
  get baseDestroyed() {
    return this._baseDestroyed;
  }

  /** Get the raw grid (read-only reference). */
  get grid() {
    return this._grid;
  }
}

module.exports = MapManager;
