import { Scene } from 'phaser';
import { Player } from '../entities/Player.js';
import { Platform } from '../entities/Platform.js';
import { PlatformManager } from '../managers/PlatformManager.js';
import { ScoreManager } from '../managers/ScoreManager.js';
import { AchievementsManager } from '../managers/AchievementsManager.js';
import { EffectsManager } from '../managers/EffectsManager.js';
import { ParticleManager } from '../managers/ParticleManager.js';
import { StatsManager } from '../managers/StatsManager.js';
import { sound } from '../managers/SoundManager.js';
import { storage, KEYS } from '../utils/storage.js';
import { rng } from '../utils/random.js';
import { currentBgTexture } from '../utils/background.js';
import { GAME_WIDTH, GAME_HEIGHT, SCORE, PHYSICS } from '../config/game.config.js';

export class GameScene extends Scene {
  constructor() {
    super({ key: 'GameScene' });
  }

  init(data) {
    this.mode = (data && data.mode) || 'normal';
    this.seed = (data && data.seed) || `${Date.now()}-${Math.random()}`;
  }

  create() {
    sound.init();
    sound.resume();

    // Seed gameplay RNG so a given seed produces a reproducible run (Daily).
    rng.seed(this.seed);

    this.bg = this.add.tileSprite(GAME_WIDTH / 2, GAME_HEIGHT / 2, GAME_WIDTH, GAME_HEIGHT, currentBgTexture())
      .setScrollFactor(0)
      .setDepth(-10);

    // Color overlay for height-based tint shift
    this.bgTint = this.add.rectangle(GAME_WIDTH / 2, GAME_HEIGHT / 2, GAME_WIDTH, GAME_HEIGHT, 0x000033, 0)
      .setScrollFactor(0).setDepth(-9);

    this.particles = new ParticleManager(this);

    this.createGweiParticles();

    this.cursors = this.input.keyboard.createCursorKeys();
    this.wasd = this.input.keyboard.addKeys({ up: 'W', down: 'S', left: 'A', right: 'D' });
    this.escKey = this.input.keyboard.addKey('ESC');
    this.touchLeft = false;
    this.touchRight = false;
    this.setupTouchControls();

    const startY = GAME_HEIGHT - 80;
    this.startPlatform = new Platform(this, GAME_WIDTH / 2, startY, 'stable');
    this.startPlatform.setTint(0xa78bfa);

    this.player = new Player(this, GAME_WIDTH / 2, startY - 140);
    this.lastJumpY = this.player.y;

    this.effects = new EffectsManager(this);

    this.platformManager = new PlatformManager(this);
    this.achievements = new AchievementsManager(this);
    this.stats = new StatsManager(this);
    this.scoreManager = new ScoreManager(this);

    for (let i = 0; i < 10; i++) {
      this.platformManager.spawnPlatform(0);
    }

    this.physics.add.collider(this.player, this.startPlatform, this.handlePlatformCollision, this.platformCollisionFilter, this);
    this.physics.add.collider(this.player, this.platformManager.getPlatforms(), this.handlePlatformCollision, this.platformCollisionFilter, this);
    this.physics.add.overlap(this.player, this.platformManager.getPowerups(), this.handlePowerup, null, this);
    this.physics.add.overlap(this.player, this.platformManager.getEnemies(), this.handleEnemy, null, this);

    this.cameras.main.setBounds(0, -999999, GAME_WIDTH, 999999 + GAME_HEIGHT);
    // Doodle-jump camera: free movement below the trigger line, camera only
    // scrolls up when player rises above it, and never moves down.
    this.cameraTriggerY = GAME_HEIGHT * 0.42; // ~358 of 854 (just above middle)
    this.cameras.main.setRoundPixels(false);

    this.isGameOver = false;
    this.isPaused = false;
    this.difficultyLevel = 0;
    this.minScrollY = this.cameras.main.scrollY;

    this.onMilestone = () => sound.milestone();

    this.createPauseUI();
    this.createMuteButton();

    if (this.mode === 'daily') {
      this.add.text(GAME_WIDTH / 2, 18, `DAILY · ${this.seed}`, {
        fontFamily: '"Press Start 2P", monospace',
        fontSize: '9px',
        color: '#ffd700',
      }).setOrigin(0.5, 0).setScrollFactor(0).setDepth(300);
    }

    this.escKey.on('down', () => this.togglePause());

    // Auto-pause when the tab/window is hidden — avoids a delta-spike teleport
    // on refocus. Player must resume manually (not auto-resumed).
    this._onHidden = () => {
      if (!this.isGameOver && !this.isPaused) this.togglePause();
    };
    this.game.events.on('hidden', this._onHidden);
    this.events.once('shutdown', this.cleanup, this);

    this.maybeShowTutorial();
    this.showTouchIndicators();
  }

  cleanup() {
    if (this._onHidden) {
      this.game.events.off('hidden', this._onHidden);
      this._onHidden = null;
    }
  }

  update(time, delta) {
    if (this.isGameOver || this.isPaused) return;

    this.player.update(this.cursors, this.wasd, this.touchLeft, this.touchRight, time, delta);

    // Background is a fixed full-screen image (no tilePositionY scroll — the
    // gradient would otherwise wrap with a visible seam).

    // Cap downward speed so the player can never tunnel through a thin platform
    // in a single physics step (does not affect upward boost velocities).
    if (this.player.body.velocity.y > PHYSICS.maxFallSpeed) {
      this.player.setVelocityY(PHYSICS.maxFallSpeed);
    }

    // Camera: latch player at trigger line going up, free movement otherwise.
    const targetScrollY = this.player.y - this.cameraTriggerY;
    if (targetScrollY < this.cameras.main.scrollY) {
      this.cameras.main.scrollY = targetScrollY;
    }

    this.minScrollY = Math.min(this.minScrollY, this.cameras.main.scrollY);
    const killLine = this.minScrollY + GAME_HEIGHT;

    const height = Math.max(0, GAME_HEIGHT - this.player.y);
    this.difficultyLevel = height;

    // Background darkens as player climbs higher (cosmic feel)
    const tintAlpha = Math.min(0.5, height / 8000);
    this.bgTint.fillAlpha = tintAlpha;
    this.platformManager.update(this.difficultyLevel, killLine);
    this.scoreManager.update(this.player.y);

    if (this.player.body.bottom > killLine) {
      this.gameOver();
      return;
    }

    if (this.player.body.velocity.y > 0 && this.player.y > this.lastJumpY + SCORE.fallResetDistance) {
      this.scoreManager.onFall();
      this.lastJumpY = this.player.y;
    }

    this.effects.update(this.player, delta);
  }

  platformCollisionFilter(player, platform) {
    if (!platform || !platform.body) return false;
    if (typeof platform.isBroken === 'function' && platform.isBroken()) return false;
    if (player.body.velocity.y <= 0) return false;
    // Swept one-way check: only bounce if the player's feet were at/above the
    // platform top on the PREVIOUS step (i.e. genuinely landing from above).
    // Using deltaY makes this correct at any fall speed.
    const prevBottom = player.body.bottom - player.body.deltaY();
    return prevBottom <= platform.body.top + PHYSICS.landTolerance;
  }

  handlePlatformCollision(player, platform) {
    if (this.isGameOver) return;
    if (platform && typeof platform.onPlayerLand === 'function') {
      platform.onPlayerLand(player);
      this.scoreManager.onLand(platform.y, platform.platformType || 'stable');
      if (platform.platformType === 'breaking') sound.break();
    }
    this.lastJumpY = player.y;
    if (player.jump()) {
      sound.jump();
      if (this.stats) this.stats.onJump();
    }
    this.createJumpParticles(player.x, player.y + player.displayHeight / 2 + 3);
  }

  handlePowerup(player, powerup) {
    if (this.isGameOver) return;
    if (powerup && typeof powerup.onPlayerTouch === 'function') {
      const px = powerup.x;
      const py = powerup.y;
      const name = powerup.constructor.name.toLowerCase();
      const map = { spring: 'spring', propellerhat: 'propeller', rocket: 'rocket' };
      const kind = map[name] || name;
      const consumed = powerup.onPlayerTouch(player);
      if (consumed) {
        this.createPowerupParticles(px, py);
        this.flashScreen();
        if (kind === 'spring') {
          sound.spring();
          this.effects.startBoost(player, 'spring');
        } else if (kind === 'propeller' || kind === 'rocket') {
          sound.powerup();
          this.effects.startBoost(player, kind);
        } else {
          sound.powerup();
        }
        if (this.achievements) this.achievements.onPowerup(kind);
      }
    }
  }

  handleEnemy(player, enemy) {
    if (this.isGameOver) return;
    if (player.state === 'rocket') {
      this.createExplosion(enemy.x, enemy.y);
      enemy.destroy();
      sound.stomp();
      return;
    }
    if (player.state === 'propeller') {
      player.endPowerUp();
      this.createExplosion(enemy.x, enemy.y);
      enemy.destroy();
      sound.stomp();
      return;
    }
    if (player.body.velocity.y > 0 && player.body.bottom <= enemy.body.top + PHYSICS.stompTolerance) {
      this.createExplosion(enemy.x, enemy.y);
      enemy.destroy();
      player.jump();
      sound.stomp();
      this.scoreManager.addPoints(SCORE.stompBonus);
      if (this.achievements) this.achievements.onEnemyStomp();
      if (this.stats) this.stats.onStomp();
      this.createJumpParticles(player.x, player.y + player.displayHeight / 2 + 3);
      return;
    }
    this.gameOver(enemy.x, enemy.y);
  }

  gameOver(ex, ey) {
    if (this.isGameOver) return;
    this.isGameOver = true;
    this.player.die();
    sound.death();
    this.cameras.main.shake(300, 0.012);
    const bx = ex ?? this.player.x;
    const by = ey ?? this.player.y;
    this.createExplosion(bx, by);
    const isNewBest = this.scoreManager.saveBest();
    if (isNewBest) {
      this.time.delayedCall(400, () => sound.newBest());
    }
    const score = this.scoreManager.score;
    const blockHeight = this.scoreManager.blockHeight;

    if (this.stats) this.stats.flush(blockHeight);

    // Daily challenge keeps its own per-day best.
    let dailyBest = null;
    if (this.mode === 'daily') {
      const key = KEYS.dailyPrefix + this.seed;
      dailyBest = storage.getInt(key, 0);
      if (score > dailyBest) {
        dailyBest = score;
        storage.setItem(key, score);
      }
    }

    this.time.delayedCall(1500, () => {
      this.scoreManager.destroy();
      this.scene.start('GameOverScene', {
        score, blockHeight, isNewBest, mode: this.mode, dailyBest,
      });
    });
  }

  togglePause() {
    if (this.isGameOver) return;
    this.isPaused = !this.isPaused;
    if (this.isPaused) {
      this.physics.pause();
      this.pauseOverlay.setVisible(true);
      sound.click();
    } else {
      this.physics.resume();
      this.pauseOverlay.setVisible(false);
      sound.click();
    }
  }

  createPauseUI() {
    const { width, height } = this.scale;
    const container = this.add.container(0, 0).setScrollFactor(0).setDepth(500).setVisible(false);

    const dim = this.add.rectangle(width / 2, height / 2, width, height, 0x000000, 0.65);
    const panel = this.add.rectangle(width / 2, height / 2, 320, 280, 0x1a0533).setStrokeStyle(3, 0xa855f7);

    const title = this.add.text(width / 2, height / 2 - 90, 'PAUSED', {
      fontFamily: '"Press Start 2P", monospace',
      fontSize: '22px',
      color: '#d8b4fe',
    }).setOrigin(0.5);

    const resume = this.add.rectangle(width / 2, height / 2 - 20, 220, 44, 0x581c87)
      .setStrokeStyle(2, 0xa855f7).setInteractive({ useHandCursor: true });
    const resumeLabel = this.add.text(width / 2, height / 2 - 20, 'RESUME', {
      fontFamily: '"Press Start 2P", monospace', fontSize: '13px', color: '#fff',
    }).setOrigin(0.5);
    resume.on('pointerdown', () => this.togglePause());

    const quit = this.add.rectangle(width / 2, height / 2 + 40, 220, 44, 0x581c87)
      .setStrokeStyle(2, 0xa855f7).setInteractive({ useHandCursor: true });
    const quitLabel = this.add.text(width / 2, height / 2 + 40, 'MAIN MENU', {
      fontFamily: '"Press Start 2P", monospace', fontSize: '13px', color: '#fff',
    }).setOrigin(0.5);
    quit.on('pointerdown', () => {
      sound.click();
      this.physics.resume();
      this.scoreManager.destroy();
      this.scene.start('MenuScene');
    });

    const hint = this.add.text(width / 2, height / 2 + 100, 'ESC to resume', {
      fontFamily: '"Press Start 2P", monospace', fontSize: '9px', color: '#888',
    }).setOrigin(0.5);

    container.add([dim, panel, title, resume, resumeLabel, quit, quitLabel, hint]);
    this.pauseOverlay = container;

    const pauseBtn = this.add.text(GAME_WIDTH - 16, 50, '⏸', {
      fontSize: '22px',
    }).setOrigin(1, 0.5).setScrollFactor(0).setDepth(300).setInteractive({ useHandCursor: true });
    pauseBtn.on('pointerdown', () => this.togglePause());
  }

  createMuteButton() {
    const icon = this.add.text(GAME_WIDTH - 16, 80, sound.isMuted() ? '🔇' : '🔊', {
      fontSize: '18px',
    }).setOrigin(1, 0.5).setScrollFactor(0).setDepth(300).setInteractive({ useHandCursor: true });
    icon.on('pointerdown', () => {
      const next = !sound.isMuted();
      sound.setMuted(next);
      icon.setText(next ? '🔇' : '🔊');
      if (!next) sound.click();
    });
  }

  maybeShowTutorial() {
    if (storage.getItem(KEYS.tutorialSeen, '0') === '1') return;

    const { width, height } = this.scale;
    this.isPaused = true;
    this.physics.pause();

    const container = this.add.container(0, 0).setScrollFactor(0).setDepth(600);
    const dim = this.add.rectangle(width / 2, height / 2, width, height, 0x000000, 0.75);
    const panel = this.add.rectangle(width / 2, height / 2, 380, 480, 0x1a0533).setStrokeStyle(3, 0xa855f7);

    const title = this.add.text(width / 2, height * 0.22, 'HOW TO PLAY', {
      fontFamily: '"Press Start 2P", monospace',
      fontSize: '18px',
      color: '#d8b4fe',
    }).setOrigin(0.5);

    const lines = [
      '← →  / A D  — Move',
      'TAP LEFT / RIGHT — Move',
      'AUTO-JUMP on platforms',
      '',
      'PURPLE — Stable platform',
      'GOLD — Genesis (x2 bonus)',
      'GREY — Breaks on landing',
      'MOVING — Slides side to side',
      '',
      'Avoid BUGS, grab POWER-UPS',
      'ESC — Pause',
    ];

    const texts = lines.map((l, i) => this.add.text(width / 2, height * 0.30 + i * 24, l, {
      fontFamily: '"Press Start 2P", monospace',
      fontSize: '10px',
      color: l.includes('Genesis') ? '#ffd700' : l.includes('Breaks') ? '#999' : '#d8b4fe',
      align: 'center',
    }).setOrigin(0.5));

    const startBtn = this.add.rectangle(width / 2, height * 0.85, 220, 48, 0x581c87)
      .setStrokeStyle(2, 0xa855f7).setInteractive({ useHandCursor: true });
    const startLabel = this.add.text(width / 2, height * 0.85, "LET'S JUMP!", {
      fontFamily: '"Press Start 2P", monospace', fontSize: '14px', color: '#fff',
    }).setOrigin(0.5);

    container.add([dim, panel, title, ...texts, startBtn, startLabel]);

    startBtn.on('pointerdown', () => {
      sound.click();
      storage.setItem(KEYS.tutorialSeen, '1');
      container.destroy();
      this.isPaused = false;
      this.physics.resume();
    });
  }

  showTouchIndicators() {
    if (!this.sys.game.device.input.touch) return;
    const { width, height } = this.scale;

    const left = this.add.rectangle(width / 4, height - 100, width / 2 - 20, 60, 0xa855f7, 0.15)
      .setScrollFactor(0).setDepth(250);
    const leftText = this.add.text(width / 4, height - 100, '◀ TAP', {
      fontFamily: '"Press Start 2P", monospace', fontSize: '12px', color: '#fff',
    }).setOrigin(0.5).setScrollFactor(0).setDepth(251).setAlpha(0.7);

    const right = this.add.rectangle(3 * width / 4, height - 100, width / 2 - 20, 60, 0xa855f7, 0.15)
      .setScrollFactor(0).setDepth(250);
    const rightText = this.add.text(3 * width / 4, height - 100, 'TAP ▶', {
      fontFamily: '"Press Start 2P", monospace', fontSize: '12px', color: '#fff',
    }).setOrigin(0.5).setScrollFactor(0).setDepth(251).setAlpha(0.7);

    this.tweens.add({
      targets: [left, right, leftText, rightText],
      alpha: 0,
      delay: 3500,
      duration: 1500,
      onComplete: () => {
        left.destroy(); right.destroy();
        leftText.destroy(); rightText.destroy();
      },
    });
  }

  flashScreen() {
    const flash = this.add.rectangle(GAME_WIDTH / 2, GAME_HEIGHT / 2, GAME_WIDTH, GAME_HEIGHT, 0xffffff, 0.3);
    flash.setScrollFactor(0);
    flash.setDepth(90);
    this.tweens.add({
      targets: flash,
      alpha: 0,
      duration: 250,
      onComplete: () => flash.destroy(),
    });
  }

  setupTouchControls() {
    const { width } = this.scale;
    this.input.on('pointerdown', (pointer) => {
      if (pointer.x < width / 2) this.touchLeft = true;
      else this.touchRight = true;
    });
    this.input.on('pointerup', () => {
      this.touchLeft = false;
      this.touchRight = false;
    });
    this.input.on('pointermove', (pointer) => {
      if (pointer.isDown) {
        this.touchLeft = pointer.x < width / 2;
        this.touchRight = pointer.x >= width / 2;
      }
    });
  }

  createGweiParticles() {
    for (let i = 0; i < 25; i++) {
      const p = this.add.image(
        Phaser.Math.Between(0, GAME_WIDTH),
        Phaser.Math.Between(0, GAME_HEIGHT),
        'gwei'
      ).setAlpha(0.5).setScrollFactor(0.3).setDepth(-8);
      this.tweens.add({
        targets: p,
        y: p.y - Phaser.Math.Between(100, 400),
        alpha: 0,
        duration: Phaser.Math.Between(3000, 7000),
        repeat: -1,
        delay: Phaser.Math.Between(0, 4000),
        onRepeat: () => {
          p.y = Phaser.Math.Between(0, GAME_HEIGHT);
          p.x = Phaser.Math.Between(0, GAME_WIDTH);
          p.setAlpha(0.5);
        },
      });
    }
  }

  createJumpParticles(x, y) {
    this.particles.burstJump(x, y);
  }

  createPowerupParticles(x, y) {
    this.particles.burstPowerup(x, y);
  }

  createExplosion(x, y) {
    this.particles.burstExplosion(x, y);
  }
}
