import Phaser from 'phaser';
import { Player } from '@/entities/Player';
import { Patroller, Jumper, Flyer, Chaser, BaseEnemy } from '@/entities/Enemy';
import { Coin } from '@/entities/Coin';
import { Projectile } from '@/entities/Projectile';
import { FallingPlatform, Saw } from '@/entities/hazards';
import {
  BasePowerUp,
  ShieldPowerUp,
  SpeedPowerUp,
  SlowMoPowerUp,
  PowerUpType,
} from '@/entities/powerups';
import { ParticleManager } from '@/systems/ParticleManager';
import { ParallaxBackground } from '@/systems/ParallaxBackground';
import { UpgradeManager } from '@/systems/UpgradeManager';
import { ComboManager } from '@/systems/ComboManager';
import { audio } from '@/systems/AudioManager';
import { LevelGenerator } from '@/systems/LevelGenerator';
import { RunState, MetaState } from '@/types';
import { isScreenShakeEnabled } from './SettingsScene';

export class GameScene extends Phaser.Scene {
  public player!: Player;
  private platforms!: Phaser.Physics.Arcade.StaticGroup;
  private enemies!: Phaser.Physics.Arcade.Group;
  private coins!: Phaser.Physics.Arcade.Group;
  private projectiles!: Phaser.Physics.Arcade.Group;
  private spikes!: Phaser.Physics.Arcade.StaticGroup;
  private fallingPlatforms!: FallingPlatform[];
  private saws!: Phaser.Physics.Arcade.Group;
  private powerUps!: Phaser.Physics.Arcade.Group;

  // Active power-up effects
  private activeEffects: Map<PowerUpType, number> = new Map();

  private runState!: RunState;
  public particles!: ParticleManager;
  public upgrades!: UpgradeManager;
  public combo!: ComboManager;
  private parallaxBg!: ParallaxBackground;

  constructor() {
    super({ key: 'GameScene' });
  }

  create(): void {
    this.runState = this.registry.get('runState') as RunState;
    const metaState = this.registry.get('metaState') as MetaState;

    // Инициализируем системы
    this.particles = new ParticleManager(this);
    this.upgrades = new UpgradeManager(metaState);
    this.combo = new ComboManager((combo, multiplier) => {
      this.events.emit('comboChanged', { combo, multiplier });
    });

    // Применяем апгрейды к runState
    const projectileCount = this.upgrades.getProjectileCount();
    this.runState.projectilesAvailable = projectileCount;
    this.runState.projectileCooldowns = new Array(projectileCount).fill(0);

    // Create parallax background (before everything else for proper depth)
    this.parallaxBg = new ParallaxBackground(this);

    // Create groups
    this.platforms = this.physics.add.staticGroup();
    this.enemies = this.physics.add.group();
    this.coins = this.physics.add.group({ allowGravity: false });
    this.projectiles = this.physics.add.group({ allowGravity: false });
    this.spikes = this.physics.add.staticGroup();
    this.fallingPlatforms = [];
    this.saws = this.physics.add.group({ allowGravity: false });
    this.powerUps = this.physics.add.group({ allowGravity: false });
    this.activeEffects.clear();

    // Create player (spawn on ground) - before enemies so Chaser can reference player
    // Spawn at safe position away from spikes (spikes start at x=200)
    this.player = new Player(this, 80, 600);

    // Create test level
    this.createTestLevel();

    // Setup collisions
    this.setupCollisions();

    // Launch UI scene
    this.scene.launch('UIScene');

    // Очищаем старые обработчики и добавляем новые
    this.events.off('shoot', this.handleShoot, this);
    this.events.on('shoot', this.handleShoot, this);
  }

  update(time: number, delta: number): void {
    // Guard: проверяем что сцена активна и группы существуют
    if (!this.enemies || !this.scene.isActive()) return;

    this.player.update(time, delta);

    // Update parallax background based on player movement
    this.parallaxBg.update();

    // Update enemies (with slowmo effect)
    const enemyDelta = delta * this.getEnemySpeedMultiplier();
    this.enemies.getChildren().forEach((child) => {
      const enemy = child as unknown as BaseEnemy;
      enemy.update(time, enemyDelta);
    });

    // Update projectiles
    this.projectiles.getChildren().forEach((child) => {
      const proj = child as unknown as Projectile;
      proj.update(time, delta);
    });

    // Update falling platforms
    this.fallingPlatforms.forEach((platform) => {
      platform.update(time, delta);
    });

    // Update saws (with slowmo effect)
    this.saws.getChildren().forEach((child) => {
      const saw = child as unknown as Saw;
      saw.update(time, enemyDelta);
    });

    // Update projectile cooldowns
    this.updateProjectileCooldowns(delta);

    // Update power-up effects
    this.updatePowerEffects(delta);

    // Update combo timer
    this.combo.update(delta);

    // Магнит монет
    this.updateCoinMagnet();

    // Update run state time
    this.runState.timeElapsed += delta;

    // Check level complete
    if (
      this.runState.totalCoinsOnLevel > 0 &&
      this.runState.coins >= this.runState.totalCoinsOnLevel
    ) {
      this.completeLevel();
    }
  }

  private createTestLevel(): void {
    const level = this.runState.currentLevel;

    // Ground
    for (let x = 0; x < 40; x++) {
      this.platforms.create(x * 32 + 16, 688, 'platform');
    }

    // Walls - визуальные тайлы (без коллизий)
    for (let y = 0; y < 22; y++) {
      this.add.image(16, y * 32 + 16, 'platform');
      this.add.image(1264, y * 32 + 16, 'platform');
    }

    // Walls - единые коллизионные тела (без швов между тайлами)
    const wallHeight = 22 * 32;
    const leftWall = this.add.rectangle(16, wallHeight / 2, 32, wallHeight, 0x000000, 0);
    const rightWall = this.add.rectangle(1264, wallHeight / 2, 32, wallHeight, 0x000000, 0);
    this.physics.add.existing(leftWall, true);
    this.physics.add.existing(rightWall, true);
    this.platforms.add(leftWall);
    this.platforms.add(rightWall);

    // Generate level using procedural generator
    const generator = new LevelGenerator(level);
    const layout = generator.generate();

    // Create platforms
    layout.platforms.forEach((p) => {
      if (p.isFalling) {
        for (let i = 0; i < p.width; i++) {
          this.createFallingPlatform(p.x + i * 32, p.y);
        }
      } else {
        this.createPlatform(p.x, p.y, p.width);
      }
    });

    // Create coins (with platform collision check)
    let coinsCreated = 0;
    layout.coinPositions.forEach((pos) => {
      if (!this.isPositionInPlatform(pos.x, pos.y)) {
        this.createCoin(pos.x, pos.y);
        coinsCreated++;
      }
    });
    this.runState.totalCoinsOnLevel = coinsCreated;

    // Create enemies
    layout.enemyPositions.forEach((e) => {
      this.createEnemy(e.x, e.y, e.type as 'patroller' | 'jumper' | 'flyer' | 'chaser');
    });

    // Create hazards
    layout.hazardPositions.forEach((h) => {
      if (h.type === 'spikes') {
        this.createSpikes(h.x, h.y, 2 + Math.floor(Math.random() * 2));
      } else if (h.type === 'saw_v') {
        this.createSaw(h.x, h.y, 80 + Math.random() * 40, true);
      } else if (h.type === 'saw_h') {
        this.createSaw(h.x, h.y, 80 + Math.random() * 40, false);
      }
    });

    // Create power-ups
    const powerUpTypes: PowerUpType[] = ['shield', 'speed', 'slowmo'];
    layout.powerUpPositions.forEach((pos, i) => {
      const type = powerUpTypes[i % powerUpTypes.length];
      this.createPowerUp(pos.x, pos.y, type);
    });
  }

  private createPlatform(x: number, y: number, width: number): void {
    for (let i = 0; i < width; i++) {
      this.platforms.create(x + i * 32, y, 'platform');
    }
  }

  private createCoin(x: number, y: number): void {
    const coin = new Coin(this, x, y);
    this.coins.add(coin);
  }

  private createEnemy(
    x: number,
    y: number,
    type: 'patroller' | 'jumper' | 'flyer' | 'chaser' = 'patroller'
  ): void {
    let enemy: BaseEnemy;

    switch (type) {
      case 'jumper':
        enemy = new Jumper(this, x, y);
        break;
      case 'flyer':
        enemy = new Flyer(this, x, y);
        break;
      case 'chaser':
        enemy = new Chaser(this, x, y);
        (enemy as Chaser).setPlayer(this.player);
        break;
      case 'patroller':
      default:
        enemy = new Patroller(this, x, y);
        break;
    }

    this.enemies.add(enemy);
  }

  private createSpikes(x: number, y: number, count: number): void {
    for (let i = 0; i < count; i++) {
      const spike = this.spikes.create(x + i * 32, y, 'spikes') as Phaser.Physics.Arcade.Sprite;
      spike.body!.setSize(32, 12);
      spike.body!.setOffset(0, 4);
    }
  }

  private createFallingPlatform(x: number, y: number): void {
    const platform = new FallingPlatform(this, x, y);
    this.fallingPlatforms.push(platform);
    this.platforms.add(platform);
  }

  private createSaw(x: number, y: number, patrolDistance = 100, vertical = false): void {
    const saw = new Saw(this, x, y, patrolDistance, vertical);
    this.saws.add(saw);
  }

  private createPowerUp(x: number, y: number, type: PowerUpType): void {
    let powerUp: BasePowerUp;

    switch (type) {
      case 'shield':
        powerUp = new ShieldPowerUp(this, x, y);
        break;
      case 'speed':
        powerUp = new SpeedPowerUp(this, x, y);
        break;
      case 'slowmo':
        powerUp = new SlowMoPowerUp(this, x, y);
        break;
    }

    this.powerUps.add(powerUp);
  }

  private isPositionInPlatform(x: number, y: number, padding: number = 8): boolean {
    const platformBodies = this.platforms.getChildren();
    for (const child of platformBodies) {
      const platform = child as Phaser.Physics.Arcade.Sprite;
      const px = platform.x;
      const py = platform.y;
      // Platform tile is 32x32, centered at (px, py)
      // padding accounts for coin size (16x16) / 2 = 8
      if (
        x >= px - 16 - padding &&
        x <= px + 16 + padding &&
        y >= py - 16 - padding &&
        y <= py + 16 + padding
      ) {
        return true;
      }
    }
    return false;
  }

  private setupCollisions(): void {
    // Enemies <-> Platforms
    this.physics.add.collider(this.enemies, this.platforms);

    // Player <-> Coins
    this.physics.add.overlap(
      this.player,
      this.coins,
      (_, coinObj) => this.collectCoin(coinObj as Coin),
      undefined,
      this
    );

    // Player <-> Active Enemies
    this.physics.add.overlap(
      this.player,
      this.enemies,
      (_, enemyObj) => this.handleEnemyCollision(enemyObj as unknown as BaseEnemy),
      undefined,
      this
    );

    // Player <-> Spikes
    this.physics.add.overlap(
      this.player,
      this.spikes,
      () => {
        if (!this.hasEffect('shield')) this.handleDeath();
      },
      undefined,
      this
    );

    // Player <-> Saws
    this.physics.add.overlap(
      this.player,
      this.saws,
      () => {
        if (!this.hasEffect('shield')) this.handleDeath();
      },
      undefined,
      this
    );

    // Player <-> Falling Platforms (trigger fall)
    this.physics.add.collider(
      this.player,
      this.platforms,
      (_, platformObj) => {
        if (platformObj instanceof FallingPlatform) {
          platformObj.trigger();
        }
      },
      undefined,
      this
    );

    // Player <-> Power-ups
    this.physics.add.overlap(
      this.player,
      this.powerUps,
      (_, powerUpObj) => this.collectPowerUp(powerUpObj as unknown as BasePowerUp),
      undefined,
      this
    );

    // Projectiles <-> Enemies
    this.physics.add.overlap(
      this.projectiles,
      this.enemies,
      (projObj, enemyObj) =>
        this.handleProjectileHit(
          projObj as unknown as Projectile,
          enemyObj as unknown as BaseEnemy
        ),
      undefined,
      this
    );

    // Projectiles <-> Platforms
    this.physics.add.collider(
      this.projectiles,
      this.platforms,
      (projObj) => this.handleProjectileWall(projObj as unknown as Projectile),
      undefined,
      this
    );
  }

  private collectCoin(coin: Coin): void {
    if (!coin.collect()) return; // Already collected

    // Звук и искры при сборе монеты
    audio.playCoin();
    this.particles.emitCoinSparkle(coin.x, coin.y);

    // Добавляем комбо и применяем множитель
    this.combo.addCombo(1);
    const scoreGain = this.combo.applyMultiplier(10);

    this.runState.coins++;
    this.runState.totalCoinsInRun++;
    this.runState.score += scoreGain;

    this.events.emit('coinCollected', {
      coins: this.runState.coins,
      total: this.runState.totalCoinsOnLevel,
      scoreGain,
    });
  }

  private handleEnemyCollision(enemy: BaseEnemy): void {
    if (enemy.isActive() && !this.hasEffect('shield')) {
      this.handleDeath();
    }
  }

  private collectPowerUp(powerUp: BasePowerUp): void {
    if (!powerUp.collect()) return;

    audio.playCoin(); // Reuse coin sound for now
    this.particles.emitCoinSparkle(powerUp.x, powerUp.y);

    // Activate effect
    this.activeEffects.set(powerUp.powerUpType, powerUp.getDuration());

    // Visual feedback based on type
    if (powerUp.powerUpType === 'shield') {
      this.player.setTint(0x44aaff);
    } else if (powerUp.powerUpType === 'speed') {
      this.player.setTint(0xffff44);
    }
  }

  private updatePowerEffects(delta: number): void {
    this.activeEffects.forEach((remaining, type) => {
      const newRemaining = remaining - delta;
      if (newRemaining <= 0) {
        this.activeEffects.delete(type);
        this.onEffectExpired(type);
      } else {
        this.activeEffects.set(type, newRemaining);
      }
    });
  }

  private onEffectExpired(type: PowerUpType): void {
    if (type === 'shield' || type === 'speed') {
      this.player.clearTint();
    }
  }

  public hasEffect(type: PowerUpType): boolean {
    return this.activeEffects.has(type);
  }

  public getSpeedMultiplier(): number {
    return this.hasEffect('speed') ? 1.5 : 1.0;
  }

  public getEnemySpeedMultiplier(): number {
    return this.hasEffect('slowmo') ? 0.3 : 1.0;
  }

  private handleDeath(): void {
    if (this.player.isDead) return;

    // Звук и взрыв при смерти
    audio.playDeath();
    this.particles.emitDeathExplosion(this.player.x, this.player.y);

    this.player.die();
    if (isScreenShakeEnabled()) {
      this.cameras.main.shake(200, 0.01);
    }

    this.time.delayedCall(800, () => {
      const sceneManager = this.game.scene;
      sceneManager.stop('UIScene');
      sceneManager.stop('GameScene');
      sceneManager.start('DeathScene');
    });
  }

  private handleShoot(direction: number): void {
    // Find first available projectile (cooldown <= 0)
    const index = this.runState.projectileCooldowns.findIndex((cd) => cd <= 0);
    if (index === -1) return; // No projectiles available

    const shootX = this.player.x + direction * 20;
    const shootY = this.player.y;

    // Звук и облако при выстреле
    audio.playShoot();
    this.particles.emitShootCloud(shootX, shootY, direction);

    const proj = new Projectile(this, shootX, shootY, direction);
    this.projectiles.add(proj);
    proj.launch();

    // Start cooldown for this slot (с учётом апгрейда)
    this.runState.projectileCooldowns[index] = this.upgrades.getProjectileCooldown();
  }

  private updateCoinMagnet(): void {
    const magnetRadius = this.upgrades.getCoinMagnetRadius();

    const playerX = this.player.x;
    const playerY = this.player.y;

    this.coins.getChildren().forEach((child) => {
      const coin = child as Coin;
      if (!coin.active) return;

      const dx = playerX - coin.x;
      const dy = playerY - coin.y;
      const distance = Math.sqrt(dx * dx + dy * dy);

      const isInRange = magnetRadius > 0 && distance < magnetRadius && distance > 5;

      if (isInRange) {
        // Притягиваем монету к игроку
        const speed = 300;
        const vx = (dx / distance) * speed;
        const vy = (dy / distance) * speed;
        coin.magnetize(vx, vy);
      } else {
        // Обновляем состояние магнита (возврат к оригинальной позиции если нужно)
        coin.updateMagnetState(false);
      }
    });
  }

  private updateProjectileCooldowns(delta: number): void {
    for (let i = 0; i < this.runState.projectileCooldowns.length; i++) {
      if (this.runState.projectileCooldowns[i] > 0) {
        this.runState.projectileCooldowns[i] -= delta;
        if (this.runState.projectileCooldowns[i] < 0) {
          this.runState.projectileCooldowns[i] = 0;
        }
      }
    }
  }

  private handleProjectileHit(projectile: Projectile, enemy: BaseEnemy): void {
    if (enemy.isActive()) {
      // Звук и звёзды при оглушении
      audio.playStun();
      this.particles.emitStunStars(enemy.x, enemy.y);

      // Добавляем комбо за оглушение (даёт больше чем монета)
      this.combo.addCombo(2);
      const scoreGain = this.combo.applyMultiplier(5);

      // Оглушение с учётом апгрейда
      enemy.stun(this.upgrades.getStunDuration());
      this.runState.enemiesStunned++;
      this.runState.score += scoreGain;

      // Hitstop effect
      this.physics.pause();
      this.time.delayedCall(50, () => {
        this.physics.resume();
      });

      if (isScreenShakeEnabled()) {
        this.cameras.main.shake(50, 0.002);
      }
    }

    projectile.destroy();
  }

  private handleProjectileWall(projectile: Projectile): void {
    projectile.destroy();
  }

  private completeLevel(): void {
    // Звук завершения уровня
    audio.playLevelComplete();

    this.runState.currentLevel++;
    this.runState.score += 100 * (this.runState.currentLevel - 1);
    this.runState.coins = 0;

    // Показываем экран завершения уровня
    const sceneManager = this.game.scene;
    sceneManager.stop('GameScene');
    sceneManager.stop('UIScene');
    sceneManager.start('LevelCompleteScene');
  }
}
