import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
import 'dart:math';
import '../utils/colors.dart';
import '../utils/score_manager.dart';
import '../utils/settings_manager.dart';
import '../utils/tilt_detector.dart';
import '../utils/google_play_games_manager.dart';
import 'components/bubble.dart';
import 'components/bubble_spawner.dart';
import 'audio/audio_manager.dart';

class ZenTapGame extends FlameGame with HasCollisionDetection {
  static const String routeName = '/game';
  
  TextComponent? scoreText;
  TextComponent? timerText;
  int score = 0;
  bool isZenMode = false;
  double gameTime = 0.0;
  bool gameActive = true;
  
  // Localized strings
  String zenModeText = 'Zen Mode';
  String relaxationPointsText = 'Relaxation Points';
  String timeText = 'Time';
  
  BubbleSpawner? bubbleSpawner;
  final AudioManager audioManager = AudioManager();
  final TiltDetector _tiltDetector = TiltDetector();
  final TiltDetector tiltDetector = TiltDetector();
  final GooglePlayGamesManager _gamesManager = GooglePlayGamesManager.instance;
  
  // Google Play Games tracking
  int _totalBubblesPopped = 0;
  double _sessionStartTime = 0.0;
  bool _perfectSession = true; // No missed bubbles
  
  // Performance optimization: cache last displayed second to avoid unnecessary text updates
  int _lastDisplayedSecond = -1;

  /// Set localized strings for the game
  void setLocalizedStrings({
    required String zenMode,
    required String relaxationPoints,
    required String time,
  }) {
    zenModeText = zenMode;
    relaxationPointsText = relaxationPoints;
    timeText = time;
  }

  @override
  Color backgroundColor() => Colors.transparent;

  @override
  Future<void> onLoad() async {
    await super.onLoad();
    
    // Initialize Google Play Games
    await _gamesManager.initialize();
    
    // Track session start time
    _sessionStartTime = DateTime.now().millisecondsSinceEpoch / 1000.0;
    
    // Initialize score manager and audio
    await ScoreManager.initialize();
    await audioManager.initialize();
    
    // Apply saved audio settings after initialization
    audioManager.setMusicEnabled(SettingsManager.isMusicEnabled);
    audioManager.setHapticEnabled(SettingsManager.isHapticsEnabled);
    audioManager.setBgmVolume(SettingsManager.bgmVolume);
    audioManager.setSfxVolume(SettingsManager.sfxVolume);
    
    // Initialize score display
    scoreText = TextComponent(
      text: isZenMode ? zenModeText : '$relaxationPointsText: $score',
      textRenderer: TextPaint(
        style: const TextStyle(
          color: ZenColors.scoreText,
          fontSize: 24,
          fontWeight: FontWeight.w500,
        ),
      ),
      position: Vector2(20, 50),
    );
    add(scoreText!);
    
    // Initialize timer display (only in regular mode)
    if (!isZenMode) {
      timerText = TextComponent(
        text: '$timeText: ${_formatTime(gameTime)}',
        textRenderer: TextPaint(
          style: const TextStyle(
            color: ZenColors.timerText,
            fontSize: 18,
          ),
        ),
        position: Vector2(20, 80),
      );
      add(timerText!);
    }
    
    // Add initial game elements
    await _initializeGame();
    
    // Start background music after all initialization is complete
    await audioManager.playBackgroundMusic();
    
    // Start tilt detection
    _initializeTiltDetection();
  }
  
  double _lastTiltUpdateTime = 0.0;
  static const double tiltUpdateInterval = 0.1; // Update tilt effects max 10 times per second
  
  void _initializeTiltDetection() {
    _tiltDetector.startListening(
      onTiltChanged: (tiltAngle) {
        // Throttle tilt updates to improve performance
        final currentTime = DateTime.now().millisecondsSinceEpoch / 1000.0;
        if (currentTime - _lastTiltUpdateTime >= tiltUpdateInterval) {
          _lastTiltUpdateTime = currentTime;
          _applyTiltToAllBubbles(_tiltDetector.normalizedTilt);
        }
      },
      onShakeDetected: () {
        handleShake();
      },
    );
  }
  
  void _applyTiltToAllBubbles(double tiltStrength) async {
    if (bubbleSpawner == null) return;
    
    // Apply tilt forces asynchronously to avoid blocking main thread
    final activeBubbles = bubbleSpawner!.getActiveBubbles();
    
    // Process bubbles in smaller batches to prevent frame drops
    const batchSize = 3;
    for (int i = 0; i < activeBubbles.length; i += batchSize) {
      final endIndex = (i + batchSize).clamp(0, activeBubbles.length);
      final batch = activeBubbles.sublist(i, endIndex);
      
      for (final bubble in batch) {
        bubble.applyTiltForce(tiltStrength);
      }
      
      // Yield control back to the framework between batches
      if (endIndex < activeBubbles.length) {
        await Future.delayed(Duration.zero);
      }
    }
  }

  Future<void> _initializeGame() async {
    // Initialize bubble spawner
    bubbleSpawner = BubbleSpawner(
      onBubblePopped: _onBubblePopped,
    );
    add(bubbleSpawner!);
    
    // Instruction text removed for cleaner interface
    
    // Background music is now started in onLoad() after all initialization
  }

  @override
  void update(double dt) {
    super.update(dt);
    
    if (gameActive && !isZenMode) {
      gameTime += dt;
      
      // Performance optimization: only update timer text when the second changes
      final currentSecond = gameTime.toInt();
      if (currentSecond != _lastDisplayedSecond) {
        _lastDisplayedSecond = currentSecond;
        _updateTimer();
      }
    }
  }

  @override
  void onGameResize(Vector2 size) {
    super.onGameResize(size);
    
    // Update text positions when screen size changes
    if (scoreText != null) {
      scoreText!.position = Vector2(20, 50);
    }
    if (timerText != null) {
      timerText!.position = Vector2(20, 80);
    }
    
    // Reposition bubbles that might be off-screen
    _repositionBubblesInBounds();
  }

  void _repositionBubblesInBounds() {
    if (bubbleSpawner == null) return;
    
    const margin = 120.0;
    final minX = margin;
    final maxX = size.x - margin;
    final minY = margin + 100;
    final maxY = size.y - margin;
    
    if (maxX <= minX || maxY <= minY) return;
    
    // Performance optimization: use bubble spawner's cached active bubbles
    final activeBubbles = bubbleSpawner!.getActiveBubbles();
    for (final bubble in activeBubbles) {
      if (!bubble.isPopping) {
        bool needsRepositioning = false;
        Vector2 newPosition = bubble.position.clone();
        
        if (bubble.position.x < minX) {
          newPosition.x = minX;
          needsRepositioning = true;
        } else if (bubble.position.x > maxX) {
          newPosition.x = maxX;
          needsRepositioning = true;
        }
        
        if (bubble.position.y < minY) {
          newPosition.y = minY;
          needsRepositioning = true;
        } else if (bubble.position.y > maxY) {
          newPosition.y = maxY;
          needsRepositioning = true;
        }
        
        if (needsRepositioning) {
          bubble.position = newPosition;
        }
      }
    }
  }

  void handleTap(Vector2 position) {
    if (!gameActive) return;
    
    // Create a bubble at tap position if in zen mode or no bubble was hit
    if (isZenMode) {
      bubbleSpawner?.spawnBubbleAt(position);
    }
    
    // Add visual feedback at tap position
    _addTapEffect(position);
  }

  void _onBubblePopped(Bubble bubble, bool userTriggered) {
    // Play pop sound and haptic feedback
    audioManager.playBubblePop();
    
    // Track Google Play Games statistics
    if (userTriggered) {
      _totalBubblesPopped++;
      
      // Check for perfect session achievement after significant bubbles
      if (_totalBubblesPopped >= 50 && _perfectSession) {
        _gamesManager.unlockAchievement(
          GooglePlayGamesManager.achievementPerfectSession
        );
      }
    } else {
      // User missed a bubble (it timed out)
      _perfectSession = false;
    }
    
    if (!isZenMode && userTriggered) {
      // Only increment score for user-triggered pops in regular mode
      score += 10; // 10 points per bubble
      ScoreManager.addScore(10);
      _updateScore();
      
      // Check for speed achievements
      _gamesManager.checkScoreAchievements(score, gameTime);
    }
    
    // Check bubble-based achievements
    _gamesManager.checkBubbleAchievements(_totalBubblesPopped);
  }

  void _updateScore() {
    scoreText?.text = '$relaxationPointsText: $score';
  }

  void _updateTimer() {
    timerText?.text = '$timeText: ${_formatTime(gameTime)}';
  }

  // Helper method to format time as MM:SS
  String _formatTime(double seconds) {
    final minutes = (seconds / 60).floor();
    final remainingSeconds = (seconds % 60).floor();
    return '${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}';
  }

  void _addTapEffect(Vector2 position) {
    // Create a simple tap effect circle with animation
    final tapEffect = CircleComponent(
      radius: 15,
      paint: Paint()
        ..color = ZenColors.bubblePopEffect.withValues(alpha: 0.6)
        ..style = PaintingStyle.stroke
        ..strokeWidth = 2,
      position: position,
      anchor: Anchor.center,
    );
    
    add(tapEffect);
    
    // Add scaling animation
    tapEffect.add(
      ScaleEffect.to(
        Vector2.all(2.0),
        EffectController(duration: 0.3),
      ),
    );
    
    // Add fade animation
    tapEffect.add(
      OpacityEffect.to(
        0.0,
        EffectController(duration: 0.3),
      ),
    );
    
    // Remove the effect after animation
    Future.delayed(const Duration(milliseconds: 300), () {
      if (tapEffect.isMounted) {
        remove(tapEffect);
      }
    });
  }

  void setZenMode(bool zenMode) {
    isZenMode = zenMode;
    if (scoreText != null) {
      if (zenMode) {
        scoreText!.text = zenModeText;
        score = 0;
        // Hide timer in zen mode
        timerText?.removeFromParent();
        timerText = null;
      } else {
        scoreText!.text = '$relaxationPointsText: $score';
        // Show timer in regular mode
        if (timerText == null) {
          timerText = TextComponent(
            text: '$timeText: ${_formatTime(gameTime)}',
            textRenderer: TextPaint(
              style: const TextStyle(
                color: ZenColors.timerText,
                fontSize: 18,
              ),
            ),
            position: Vector2(20, 80),
          );
          add(timerText!);
        }
      }
    }
    
    // Update bubble spawner behavior
    bubbleSpawner?.setActive(!zenMode);
  }

  void resetGame() {
    score = 0;
    gameTime = 0.0;
    gameActive = true;
    _updateScore();
    _updateTimer();
    
    // Clear all bubbles
    bubbleSpawner?.clearAllBubbles();
  }

  void pauseGame() {
    paused = true;
    gameActive = false;
    audioManager.pauseBackgroundMusic();
    _tiltDetector.stopListening();
  }

  void resumeGame() {
    paused = false;
    gameActive = true;
    audioManager.resumeBackgroundMusic();
    _initializeTiltDetection();
  }
  
  /// End the game session and submit results to Google Play Games
  Future<void> endGameSession() async {
    gameActive = false;
    
    final currentTime = DateTime.now().millisecondsSinceEpoch / 1000.0;
    final sessionLength = currentTime - _sessionStartTime;
    
    // Check for zen mode achievements
    if (isZenMode) {
      await _gamesManager.checkZenModeAchievements(sessionLength);
    }
    
    // Submit final results to Google Play Games
    await _gamesManager.submitGameResults(
      finalScore: score,
      isZenMode: isZenMode,
      totalBubblesPopped: _totalBubblesPopped,
      sessionLength: sessionLength,
    );
    
    // Score is already tracked by ScoreManager.addScore() calls during gameplay
  }

  void handleShake() {
    if (!gameActive) return;
    
    // Get existing bubbles before spawning new ones
    final existingBubbles = bubbleSpawner?.getActiveBubbles().toList() ?? [];
    
    // Single bubble per shake rule: spawn only ONE bubble per shake
    // Find a safe position that doesn't overlap with existing bubbles
    final safePosition = _findSafeSpawnPosition();
    if (safePosition != null) {
      bubbleSpawner?.spawnBubbleAt(safePosition);
    }
    
    // Only shake the existing bubbles (not the newly spawned ones)
    _shakeExistingBubbles(existingBubbles);
  }
  
  void _shakeExistingBubbles(List<Bubble> existingBubbles) {
    // Only apply shake effects to bubbles that existed before this shake
    for (final bubble in existingBubbles) {
      if (!bubble.isPopping) {
        bubble.addShakeEffect();
      }
    }
  }

  /// Find a safe position to spawn a bubble that doesn't overlap with existing bubbles
  Vector2? _findSafeSpawnPosition() {
    const margin = 120.0;
    const minDistance = 80.0; // Minimum distance from other bubbles
    const maxAttempts = 20; // Maximum attempts to find a safe position
    
    final random = Random();
    final activeBubbles = bubbleSpawner?.getActiveBubbles() ?? [];
    
    for (int attempt = 0; attempt < maxAttempts; attempt++) {
      // Generate random position within bounds
      final position = Vector2(
        margin + random.nextDouble() * (size.x - 2 * margin),
        margin + 100 + random.nextDouble() * (size.y - 2 * margin - 100),
      );
      
      // Check if position is safe from all existing bubbles
      bool isSafe = true;
      for (final bubble in activeBubbles) {
        if (!bubble.isPopping) {
          final distance = position.distanceTo(bubble.position);
          if (distance < minDistance) {
            isSafe = false;
            break;
          }
        }
      }
      
      if (isSafe) {
        return position;
      }
    }
    
    // If no safe position found, return null (don't spawn)
    return null;
  }

  @override
  void onRemove() {
    // End the game session and submit results
    endGameSession();
    
    audioManager.stopBackgroundMusic();
    _tiltDetector.stopListening();
    super.onRemove();
  }
}