import Phaser from 'phaser';
import { soundDirector } from '../audio/SoundDirector';
import { firstBattleMap, firstBattleUnits, type TerrainType, type UnitData } from '../data/scenario';
import { palette } from '../ui/palette';

const terrainColor: Record<TerrainType, number> = {
  plain: 0x6d8056,
  forest: 0x355f42,
  hill: 0x82724e,
  fort: 0x8b6f48
};

const unitTexture: Record<string, string> = {
  'liu-bei': 'unit-liu-bei',
  'guan-yu': 'unit-guan-yu',
  'zhang-fei': 'unit-zhang-fei',
  'rebel-a': 'unit-rebel',
  'rebel-b': 'unit-rebel',
  'rebel-c': 'unit-rebel-cavalry',
  'rebel-leader': 'unit-rebel-leader'
};

type BattleLayout = {
  mapX: number;
  mapY: number;
  mapWidth: number;
  mapHeight: number;
  gridX: number;
  gridY: number;
  gridSize: number;
  tileSize: number;
  panelX: number;
  panelY: number;
  panelWidth: number;
  panelHeight: number;
};

type UnitView = {
  sprite: Phaser.GameObjects.Image;
  label: Phaser.GameObjects.Text;
};

type BattlePhase = 'idle' | 'moving' | 'command';
type BattleCommand = 'attack' | 'strategy' | 'item' | 'wait';

type CommandButton = {
  command: BattleCommand;
  background: Phaser.GameObjects.Rectangle;
  text: Phaser.GameObjects.Text;
};

type PendingMove = {
  unit: UnitData;
  fromX: number;
  fromY: number;
  toX: number;
  toY: number;
};

const commandLabels: Record<BattleCommand, string> = {
  attack: '공격',
  strategy: '책략',
  item: '도구',
  wait: '대기'
};

export class BattleScene extends Phaser.Scene {
  private layout!: BattleLayout;
  private phase: BattlePhase = 'idle';
  private selectedUnit?: UnitData;
  private infoText?: Phaser.GameObjects.Text;
  private turnText?: Phaser.GameObjects.Text;
  private markers: Phaser.GameObjects.Rectangle[] = [];
  private commandButtons: CommandButton[] = [];
  private commandMenuObjects: Array<Phaser.GameObjects.Rectangle | Phaser.GameObjects.Text> = [];
  private unitViews = new Map<string, UnitView>();
  private actedUnitIds = new Set<string>();
  private pendingMove?: PendingMove;

  constructor() {
    super('BattleScene');
  }

  create() {
    const { width, height } = this.scale;
    this.layout = this.createLayout(width, height);
    soundDirector.playMusic('battle-prep');
    this.input.mouse?.disableContextMenu();
    this.input.on('pointerdown', (pointer: Phaser.Input.Pointer) => {
      if (pointer.rightButtonDown()) {
        this.cancelPendingMove();
      }
    });

    this.add.rectangle(0, 0, width, height, 0x080b0d).setOrigin(0);
    this.drawMap();
    this.drawUnits();
    this.drawSidePanel();
    this.setInfo('아군 차례입니다.\n행동할 장수를 선택한 뒤 파란 칸으로 이동하세요.');
  }

  private createLayout(width: number, height: number): BattleLayout {
    const margin = 24;
    const gap = 20;
    const panelWidth = Phaser.Math.Clamp(Math.floor(width * 0.27), 320, 360);
    const mapWidth = width - panelWidth - gap - margin * 2;
    const mapHeight = height - margin * 2;
    const gridSize = Math.floor(Math.min(mapHeight - 36, mapWidth - 72) / firstBattleMap.width) * firstBattleMap.width;
    const tileSize = gridSize / firstBattleMap.width;
    const mapX = margin;
    const mapY = margin;

    return {
      mapX,
      mapY,
      mapWidth,
      mapHeight,
      gridX: mapX + Math.floor((mapWidth - gridSize) / 2),
      gridY: mapY + Math.floor((mapHeight - gridSize) / 2),
      gridSize,
      tileSize,
      panelX: mapX + mapWidth + gap,
      panelY: margin,
      panelWidth,
      panelHeight: mapHeight
    };
  }

  private drawMap() {
    const layout = this.layout;
    const background = this.add.image(
      layout.mapX + layout.mapWidth / 2,
      layout.mapY + layout.mapHeight / 2,
      'battle-map-first'
    );
    const source = this.textures.get('battle-map-first').getSourceImage();
    const coverScale = Math.max(layout.mapWidth / source.width, layout.mapHeight / source.height);
    background.setScale(coverScale);
    background.setDepth(0);

    const maskShape = this.add.graphics();
    maskShape.fillStyle(0xffffff, 1);
    maskShape.fillRect(layout.mapX, layout.mapY, layout.mapWidth, layout.mapHeight);
    maskShape.setAlpha(0);
    background.setMask(maskShape.createGeometryMask());

    this.add.rectangle(layout.mapX, layout.mapY, layout.mapWidth, layout.mapHeight, 0x06090b, 0.16).setOrigin(0).setDepth(1);
    this.add
      .rectangle(layout.mapX, layout.mapY, layout.mapWidth, layout.mapHeight)
      .setOrigin(0)
      .setStrokeStyle(2, palette.gold, 0.62)
      .setDepth(8);

    firstBattleMap.terrain.forEach((row, y) => {
      row.forEach((terrain, x) => {
        const tile = this.add.rectangle(
          layout.gridX + x * layout.tileSize,
          layout.gridY + y * layout.tileSize,
          layout.tileSize,
          layout.tileSize,
          terrainColor[terrain],
          this.terrainAlpha(terrain)
        );
        tile.setOrigin(0);
        tile.setStrokeStyle(1, 0x0a1014, 0.5);
        tile.setDepth(2);
      });
    });

    this.add
      .rectangle(layout.gridX, layout.gridY, layout.gridSize, layout.gridSize)
      .setOrigin(0)
      .setStrokeStyle(2, 0xf1d489, 0.48)
      .setDepth(3);
  }

  private drawUnits() {
    firstBattleUnits.forEach((unit) => {
      const sizeRatio = unit.faction === 'ally' ? 0.98 : 0.92;
      const sprite = this.add.image(this.tileCenterX(unit.x), this.tileCenterY(unit.y), unitTexture[unit.id] ?? 'unit-rebel');
      sprite.setName(`unit-${unit.id}`);
      sprite.setDisplaySize(this.layout.tileSize * sizeRatio, this.layout.tileSize * sizeRatio);
      sprite.setDepth(6);
      sprite.setInteractive({ useHandCursor: unit.faction === 'ally' });
      sprite.on('pointerdown', () => this.selectUnit(unit));

      const label = this.add.text(sprite.x, sprite.y + this.layout.tileSize * 0.36, unit.name, {
        fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
        fontSize: '13px',
        color: unit.faction === 'ally' ? '#e7edf7' : '#ffebe7',
        backgroundColor: 'rgba(8,10,14,0.78)',
        padding: { left: 5, right: 5, top: 2, bottom: 2 }
      });
      label.setOrigin(0.5, 0);
      label.setDepth(7);
      this.unitViews.set(unit.id, { sprite, label });
    });
  }

  private drawSidePanel() {
    const { panelX, panelY, panelWidth, panelHeight } = this.layout;
    this.add.rectangle(panelX, panelY, panelWidth, panelHeight, palette.panel, 0.96).setOrigin(0);
    this.add.rectangle(panelX, panelY, 3, panelHeight, palette.gold, 0.82).setOrigin(0);

    this.add.text(panelX + 24, panelY + 28, '첫 전투', {
      fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
      fontSize: '30px',
      color: '#e8dfca',
      fontStyle: '700',
      padding: { top: 4, bottom: 4 }
    });
    this.turnText = this.add.text(panelX + 24, panelY + 88, '1턴 / 아군', {
      fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
      fontSize: '22px',
      color: '#d8b15f'
    });
    this.infoText = this.add.text(panelX + 24, panelY + 144, '', {
      fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
      fontSize: '20px',
      color: '#e8dfca',
      wordWrap: { width: panelWidth - 48, useAdvancedWrap: true },
      lineSpacing: 8
    });

    this.add.text(panelX + 24, panelY + panelHeight - 166, '승리 조건: 적 전멸', {
      fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
      fontSize: '18px',
      color: '#9aa3ad'
    });
    this.add.text(panelX + 24, panelY + panelHeight - 126, '패배 조건: 유비 퇴각', {
      fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
      fontSize: '18px',
      color: '#9aa3ad'
    });
    this.add.text(panelX + 24, panelY + panelHeight - 86, '이동 후 명령: 공격 / 책략 / 도구 / 대기', {
      fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
      fontSize: '18px',
      color: '#9aa3ad'
    });
  }

  private selectUnit(unit: UnitData) {
    if (this.phase === 'command') {
      this.setInfo('먼저 현재 장수의 행동을 결정하세요.\n공격, 책략, 도구, 대기 중 하나를 선택해야 합니다.');
      return;
    }

    if (unit.faction !== 'ally') {
      this.setInfo(`${unit.name}: ${unit.className}\nHP ${unit.hp}/${unit.maxHp}`);
      return;
    }

    if (this.actedUnitIds.has(unit.id)) {
      this.selectedUnit = undefined;
      this.clearMarkers();
      this.hideCommandMenu();
      this.phase = 'idle';
      this.setInfo(`${unit.name}은 이미 행동을 마쳤습니다.\n흑백으로 표시된 장수는 이번 턴에 다시 움직일 수 없습니다.`);
      return;
    }

    this.phase = 'moving';
    this.selectedUnit = unit;
    this.pendingMove = undefined;
    this.clearMarkers();
    this.hideCommandMenu();
    this.showMoveRange(unit);
    this.setInfo(`${unit.name} (${unit.className})\nHP ${unit.hp}/${unit.maxHp}\n공격 ${unit.attack} / 이동 ${unit.move}\n이동할 파란 칸을 선택하세요.`);
  }

  private showMoveRange(unit: UnitData) {
    for (let y = 0; y < firstBattleMap.height; y += 1) {
      for (let x = 0; x < firstBattleMap.width; x += 1) {
        if (!this.canMoveTo(unit, x, y)) {
          continue;
        }

        const marker = this.add.rectangle(
          this.layout.gridX + x * this.layout.tileSize,
          this.layout.gridY + y * this.layout.tileSize,
          this.layout.tileSize,
          this.layout.tileSize,
          palette.blue,
          0.24
        );
        marker.setOrigin(0);
        marker.setStrokeStyle(1, palette.blue, 0.68);
        marker.setDepth(4);
        marker.setInteractive({ useHandCursor: true });
        marker.on('pointerover', () => marker.setFillStyle(palette.blue, 0.36));
        marker.on('pointerout', () => marker.setFillStyle(palette.blue, 0.24));
        marker.on('pointerdown', (pointer: Phaser.Input.Pointer) => {
          if (pointer.leftButtonDown()) {
            this.moveSelectedUnit(x, y, pointer);
          }
        });
        this.markers.push(marker);
      }
    }
  }

  private moveSelectedUnit(x: number, y: number, pointer?: Phaser.Input.Pointer) {
    if (!this.selectedUnit || !this.canMoveTo(this.selectedUnit, x, y)) {
      return;
    }

    const unit = this.selectedUnit;
    const view = this.unitViews.get(unit.id);
    if (!view) {
      return;
    }

    const fromX = unit.x;
    const fromY = unit.y;
    unit.x = x;
    unit.y = y;
    this.pendingMove = { unit, fromX, fromY, toX: x, toY: y };
    this.phase = 'command';
    this.clearMarkers();
    soundDirector.playSelect();

    const targetX = this.tileCenterX(x);
    const targetY = this.tileCenterY(y);
    this.moveUnitView(unit, x, y, view);
    this.showCommandMenu(unit, pointer?.x ?? targetX, pointer?.y ?? targetY);
    this.setInfo(`${unit.name} 이동 완료\n위치: ${x + 1}, ${y + 1}\n공격, 책략, 도구, 대기 중 하나를 선택하세요.\n우클릭하면 이동을 취소합니다.`);
  }

  private canMoveTo(unit: UnitData, x: number, y: number) {
    const distance = Math.abs(unit.x - x) + Math.abs(unit.y - y);
    return (
      distance > 0 &&
      distance <= unit.move &&
      x >= 0 &&
      y >= 0 &&
      x < firstBattleMap.width &&
      y < firstBattleMap.height &&
      !this.isOccupied(x, y, unit.id)
    );
  }

  private isOccupied(x: number, y: number, exceptUnitId?: string) {
    return firstBattleUnits.some((unit) => unit.id !== exceptUnitId && unit.x === x && unit.y === y);
  }

  private terrainAlpha(terrain: TerrainType) {
    if (terrain === 'forest') {
      return 0.16;
    }
    if (terrain === 'hill') {
      return 0.13;
    }
    if (terrain === 'fort') {
      return 0.18;
    }
    return 0.03;
  }

  private tileCenterX(x: number) {
    return this.layout.gridX + x * this.layout.tileSize + this.layout.tileSize / 2;
  }

  private tileCenterY(y: number) {
    return this.layout.gridY + y * this.layout.tileSize + this.layout.tileSize / 2;
  }

  private clearMarkers() {
    this.markers.forEach((marker) => marker.destroy());
    this.markers = [];
  }

  private showCommandMenu(unit: UnitData, pointerX: number, pointerY: number) {
    this.hideCommandMenu();

    const commands: BattleCommand[] = ['attack', 'strategy', 'item', 'wait'];
    const menuWidth = 136;
    const titleHeight = 31;
    const buttonHeight = 34;
    const gap = 5;
    const padding = 8;
    const menuHeight = padding * 2 + titleHeight + commands.length * buttonHeight + (commands.length - 1) * gap;
    const { left, top } = this.commandMenuPosition(pointerX, pointerY, menuWidth, menuHeight);

    const panel = this.add.rectangle(left, top, menuWidth, menuHeight, 0x101821, 0.97);
    panel.setOrigin(0);
    panel.setStrokeStyle(2, palette.gold, 0.76);
    panel.setDepth(14);
    this.commandMenuObjects.push(panel);

    const title = this.add.text(left + menuWidth / 2, top + padding + 2, '명령', {
      fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
      fontSize: '16px',
      color: '#d8b15f',
      fontStyle: '700'
    });
    title.setOrigin(0.5, 0);
    title.setDepth(15);
    this.commandMenuObjects.push(title);

    commands.forEach((command, index) => {
      const buttonTop = top + padding + titleHeight + index * (buttonHeight + gap);
      const centerX = left + menuWidth / 2;
      const centerY = buttonTop + buttonHeight / 2;
      const background = this.add.rectangle(centerX, centerY, menuWidth - padding * 2, buttonHeight, 0x1a2630, 0.94);
      background.setStrokeStyle(1, command === 'wait' ? palette.gold : palette.blue, 0.7);
      background.setInteractive({ useHandCursor: true });
      background.setDepth(15);

      const text = this.add.text(centerX, centerY, commandLabels[command], {
        fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
        fontSize: '18px',
        color: '#f2e3bf',
        fontStyle: '700'
      });
      text.setOrigin(0.5);
      text.setDepth(16);

      const chooseCommand = (choicePointer: Phaser.Input.Pointer) => {
        if (choicePointer.rightButtonDown()) {
          this.cancelPendingMove();
          return;
        }
        if (choicePointer.leftButtonDown()) {
          this.completeUnitAction(unit, command);
        }
      };
      background.on('pointerover', () => background.setFillStyle(0x283947, 0.98));
      background.on('pointerout', () => background.setFillStyle(0x1a2630, 0.94));
      background.on('pointerdown', chooseCommand);
      text.setInteractive({ useHandCursor: true });
      text.on('pointerdown', chooseCommand);

      this.commandButtons.push({ command, background, text });
    });
  }

  private commandMenuPosition(pointerX: number, pointerY: number, width: number, height: number) {
    const margin = 14;
    const offset = 18;
    const preferredLeft = pointerX + offset + width > this.scale.width - margin ? pointerX - offset - width : pointerX + offset;
    const preferredTop = pointerY + height > this.scale.height - margin ? this.scale.height - margin - height : pointerY;

    return {
      left: Phaser.Math.Clamp(preferredLeft, margin, this.scale.width - margin - width),
      top: Phaser.Math.Clamp(preferredTop, margin, this.scale.height - margin - height)
    };
  }

  private hideCommandMenu() {
    this.commandMenuObjects.forEach((object) => object.destroy());
    this.commandMenuObjects = [];
    this.commandButtons.forEach(({ background, text }) => {
      background.destroy();
      text.destroy();
    });
    this.commandButtons = [];
  }

  private completeUnitAction(unit: UnitData, command: BattleCommand) {
    if (this.phase !== 'command' || this.selectedUnit?.id !== unit.id || this.actedUnitIds.has(unit.id)) {
      return;
    }

    this.actedUnitIds.add(unit.id);
    this.phase = 'idle';
    this.selectedUnit = undefined;
    this.pendingMove = undefined;
    this.hideCommandMenu();
    this.applyActedStyle(unit);
    soundDirector.playSelect();

    const actionMessage = this.commandResultMessage(unit, command);
    this.setInfo(`${actionMessage}\n\n${this.remainingAllyCount()}명의 아군이 아직 행동할 수 있습니다.`);
  }

  private commandResultMessage(unit: UnitData, command: BattleCommand) {
    if (command === 'attack') {
      return `${unit.name} 공격 명령 완료\n다음 구현에서 대상 선택과 피해 처리를 연결할 예정입니다.`;
    }
    if (command === 'strategy') {
      return `${unit.name} 책략 명령 완료\n책략 목록과 MP는 다음 전투 확장 때 붙이겠습니다.`;
    }
    if (command === 'item') {
      return `${unit.name} 도구 명령 완료\n도구 가방은 이후 회복 아이템과 함께 열겠습니다.`;
    }
    return `${unit.name} 대기 명령 완료`;
  }

  private remainingAllyCount() {
    return firstBattleUnits.filter((unit) => unit.faction === 'ally' && !this.actedUnitIds.has(unit.id)).length;
  }

  private cancelPendingMove() {
    if (this.phase !== 'command' || !this.pendingMove) {
      return;
    }

    const { unit, fromX, fromY, toX, toY } = this.pendingMove;
    const view = this.unitViews.get(unit.id);
    unit.x = fromX;
    unit.y = fromY;
    this.phase = 'moving';
    this.selectedUnit = unit;
    this.pendingMove = undefined;
    this.hideCommandMenu();
    soundDirector.playSelect();

    this.moveUnitView(unit, fromX, fromY, view, 180);
    this.clearMarkers();
    this.showMoveRange(unit);
    this.setInfo(`${unit.name} 이동 취소\n${toX + 1}, ${toY + 1}에서 원래 위치 ${fromX + 1}, ${fromY + 1}로 되돌렸습니다.\n다시 이동할 파란 칸을 선택하세요.`);
  }

  private moveUnitView(
    unit: UnitData,
    x: number,
    y: number,
    view = this.unitViews.get(unit.id),
    duration = 260
  ) {
    if (!view) {
      return;
    }

    const targetX = this.tileCenterX(x);
    const targetY = this.tileCenterY(y);
    this.tweens.killTweensOf([view.sprite, view.label]);
    this.tweens.add({
      targets: view.sprite,
      x: targetX,
      y: targetY,
      duration,
      ease: 'Sine.easeInOut'
    });
    this.tweens.add({
      targets: view.label,
      x: targetX,
      y: targetY + this.layout.tileSize * 0.36,
      duration,
      ease: 'Sine.easeInOut'
    });
  }

  private applyActedStyle(unit: UnitData) {
    const view = this.unitViews.get(unit.id);
    if (!view) {
      return;
    }

    view.sprite.postFX.addColorMatrix().grayscale(1);
    view.sprite.setAlpha(0.46);
    view.sprite.setTint(0xb7b7b7);
    view.label.setAlpha(0.55);
    view.label.setColor('#b9b9b9');
    view.label.setBackgroundColor('rgba(20,20,20,0.66)');
  }

  private setInfo(text: string) {
    this.infoText?.setText(text);
  }
}
