import 'dart:math';
import 'package:flame/components.dart';
import 'bubble.dart';

class BubbleSpawner extends Component with HasGameReference {
  static const double baseSpawnInterval = 1.5; // base seconds
  static const double spawnVariation = 1.0; // +/- variation in seconds
  static const int maxBubbles = 8;
  
  double _timeSinceLastSpawn = 0;
  double _nextSpawnTime = 0;
  final List<Bubble> _activeBubbles = [];
  final Random _random = Random();
  
  Function(Bubble, bool)? onBubblePopped;
  bool isActive = true;

  BubbleSpawner({this.onBubblePopped}) {
    _calculateNextSpawnTime();
  }

  double _lastCleanupTime = 0;
  static const double cleanupInterval = 0.5; // Clean up bubbles every 0.5 seconds instead of every frame

  @override
  void update(double dt) {
    super.update(dt);
    
    if (!isActive) return;
    
    _timeSinceLastSpawn += dt;
    _lastCleanupTime += dt;
    
    // Spawn new bubble if conditions are met
    if (_timeSinceLastSpawn >= _nextSpawnTime && _activeBubbles.length < maxBubbles) {
      _spawnBubble();
      _timeSinceLastSpawn = 0;
      _calculateNextSpawnTime();
    }
    
    // Clean up popped bubbles less frequently to improve performance
    if (_lastCleanupTime >= cleanupInterval) {
      _activeBubbles.removeWhere((bubble) => !bubble.isMounted);
      _lastCleanupTime = 0;
    }
  }

  void _spawnBubble() {
    if (game.size.x == 0 || game.size.y == 0) return;
    
    final spawnPosition = _getValidSpawnPosition();
    if (spawnPosition == null) return;
    
    final bubble = Bubble(
      position: spawnPosition,
      onPop: _onBubblePopped,
    );
    
    _activeBubbles.add(bubble);
    game.add(bubble);
  }

  Vector2? _getValidSpawnPosition() {
    // Calculate safe spawn area (avoiding edges and UI elements)
    // Increased margin to account for larger bubble sprites
    const margin = 120.0;
    final minX = margin;
    final maxX = game.size.x - margin;
    final minY = margin + 100; // Extra margin for score display
    final maxY = game.size.y - margin;
    
    if (maxX <= minX || maxY <= minY) return null;
    
    return Vector2(
      minX + _random.nextDouble() * (maxX - minX),
      minY + _random.nextDouble() * (maxY - minY),
    );
  }

  void _onBubblePopped(Bubble bubble, bool userTriggered) {
    _activeBubbles.remove(bubble);
    onBubblePopped?.call(bubble, userTriggered);
  }

  void spawnBubbleAt(Vector2 position) {
    // Ensure the position is within valid bounds
    final validPosition = _clampPositionToBounds(position);
    
    final bubble = Bubble(
      position: validPosition,
      onPop: _onBubblePopped,
    );
    
    _activeBubbles.add(bubble);
    game.add(bubble);
  }

  Vector2 _clampPositionToBounds(Vector2 position) {
    const margin = 120.0;
    final minX = margin;
    final maxX = game.size.x - margin;
    final minY = margin + 100;
    final maxY = game.size.y - margin;
    
    if (maxX <= minX || maxY <= minY) return position;
    
    return Vector2(
      position.x.clamp(minX, maxX),
      position.y.clamp(minY, maxY),
    );
  }

  void clearAllBubbles() {
    for (final bubble in _activeBubbles) {
      if (bubble.isMounted) {
        bubble.removeFromParent();
      }
    }
    _activeBubbles.clear();
  }

  void setActive(bool active) {
    isActive = active;
  }

  void _calculateNextSpawnTime() {
    // Random spawn time between baseSpawnInterval - spawnVariation and baseSpawnInterval + spawnVariation
    _nextSpawnTime = baseSpawnInterval + (_random.nextDouble() - 0.5) * 2 * spawnVariation;
    // Ensure minimum spawn time of 0.5 seconds
    _nextSpawnTime = _nextSpawnTime.clamp(0.5, double.infinity);
  }

  int get activeBubbleCount => _activeBubbles.length;
  
  /// Get list of active bubbles for performance optimization
  List<Bubble> getActiveBubbles() => List.unmodifiable(_activeBubbles);
}