import 'dart:math';
import 'dart:async';
import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:flame/events.dart';
import 'package:flame/collisions.dart';
import 'package:flutter/material.dart';
import '../../utils/colors.dart';

class Bubble extends SpriteComponent with HasGameReference, TapCallbacks, CollisionCallbacks {
  static const double startSize = 60.0; // Increased from 40.0
  static const double maxSize = 120.0; // Increased from 100.0
  static const double lifecycleDuration = 12.0; // seconds
  
  late String bubbleImagePath;
  late Color bubbleColor;
  bool isPopping = false;
  bool poppedByUser = false;
  Function(Bubble, bool)? onPop;
  
  double _age = 0.0;
  double _lifecycleProgress = 0.0;
  late double _targetSize;
  
  // Physics properties for collision and movement
  Vector2 velocity = Vector2.zero();
  static const double maxSpeed = 50.0;
  static const double friction = 0.98;
  static const double collisionDamping = 0.7;
  double _floatTimer = 0.0;
  
  Bubble({
    required Vector2 position,
    this.onPop,
  }) : super(
    size: Vector2.all(startSize),
    position: position,
    anchor: Anchor.center,
  ) {
    final random = Random();
    _targetSize = (maxSize * 0.6) + (random.nextDouble() * maxSize * 0.4); // 60-100% of maxSize
  }

  @override
  Future<void> onLoad() async {
    // Choose random bubble image and set corresponding color
    final bubbleData = _getRandomBubbleData();
    bubbleImagePath = bubbleData['path'];
    bubbleColor = bubbleData['color'];
    
    // Load the sprite
    sprite = await Sprite.load(bubbleImagePath);
    
    await super.onLoad();
    
    // Add circular collision hitbox
    add(CircleHitbox(
      radius: _targetSize / 2,
      anchor: Anchor.center,
    ));
    
    // Start with zero scale for smooth entrance animation
    scale = Vector2.zero();
    
    // Add smooth entrance animation
    _addEntranceAnimation();
    
    // Add varied, organic floating animations
    _addRandomFloatingAnimation();
    _addRandomRotationAnimation();
    
    // Initialize random velocity for natural movement
    final random = Random();
    velocity = Vector2(
      (random.nextDouble() - 0.5) * 20, // -10 to +10
      (random.nextDouble() - 0.5) * 20, // -10 to +10
    );
  }

  @override
  void update(double dt) {
    super.update(dt);
    
    if (isPopping) return;
    
    // Update age and lifecycle
    _age += dt;
    _lifecycleProgress = (_age / lifecycleDuration).clamp(0.0, 1.0);
    
    // Update size based on lifecycle (grow from startSize to targetSize)
    final currentSize = startSize + (_targetSize - startSize) * _lifecycleProgress;
    size = Vector2.all(currentSize);
    
    // Update opacity based on lifecycle (fade out towards the end)
    final opacityFactor = 1.0 - (_lifecycleProgress * 0.6); // Fade to 40% opacity
    opacity = opacityFactor.clamp(0.4, 1.0);
    
    // Physics-based movement
    _updatePhysics(dt);
    
    // Keep bubble within screen bounds
    _keepInBounds();
    
    // Auto-pop when lifecycle is complete
    if (_lifecycleProgress >= 1.0) {
      pop(userTriggered: false);
    }
  }
  
  void _updatePhysics(double dt) {
    // Apply velocity to position
    position.add(velocity * dt);
    
    // Apply friction
    velocity.scale(friction);
    
    // Add periodic floating effects
    _floatTimer += dt;
    if (_floatTimer > 2.0) {
      _floatTimer = 0.0;
      final random = Random();
      final floatForce = Vector2(
        (random.nextDouble() - 0.5) * 8, // -4 to +4 horizontal
        (random.nextDouble() - 0.5) * 8, // -4 to +4 vertical
      );
      velocity.add(floatForce);
    }
    
    // Clamp velocity to max speed
    if (velocity.length > maxSpeed) {
      velocity.normalize();
      velocity.scale(maxSpeed);
    }
  }
  
  void _keepInBounds() {
    const margin = 60.0;
    final gameSize = game.size;
    
    // Bounce off screen edges
    if (position.x < margin) {
      position.x = margin;
      velocity.x = velocity.x.abs(); // Bounce right
    } else if (position.x > gameSize.x - margin) {
      position.x = gameSize.x - margin;
      velocity.x = -velocity.x.abs(); // Bounce left
    }
    
    if (position.y < margin + 100) { // Account for UI at top
      position.y = margin + 100;
      velocity.y = velocity.y.abs(); // Bounce down
    } else if (position.y > gameSize.y - margin) {
      position.y = gameSize.y - margin;
      velocity.y = -velocity.y.abs(); // Bounce up
    }
  }

  Map<String, dynamic> _getRandomBubbleData() {
    final random = Random();
    final bubbleTypes = [
      {
        'path': 'bubble_blue.png',
        'color': ZenColors.defaultLink,
      },
      {
        'path': 'bubble_cyan.png',
        'color': ZenColors.buttonBackground,
      },
      {
        'path': 'bubble_green.png',
        'color': const Color(0xFF00FF80),
      },
      {
        'path': 'bubble_purple.png',
        'color': ZenColors.lightModeHover,
      },
    ];
    return bubbleTypes[random.nextInt(bubbleTypes.length)];
  }

  void _addRandomFloatingAnimation() {
    final random = Random();
    
    // Add a small upward bias to the initial velocity
    velocity.y += -5 - random.nextDouble() * 10; // -5 to -15 upward bias
    
    // The floating effect will now be handled by the physics system
    // and periodic velocity changes in the update method
  }

  void _addEntranceAnimation() {
    // Smooth scale-in animation with bounce effect
    add(
      ScaleEffect.to(
        Vector2.all(1.0),
        EffectController(
          duration: 0.6,
          curve: Curves.elasticOut,
        ),
      ),
    );
    
    // Optional fade-in animation
    opacity = 0.0;
    add(
      OpacityEffect.to(
        1.0,
        EffectController(
          duration: 0.4,
          curve: Curves.easeOut,
        ),
      ),
    );
  }

  void _addRandomRotationAnimation() {
    final random = Random();
    
    // 30% chance for rotation animation
    if (random.nextDouble() < 0.3) {
      // Small random rotation between -0.2 and 0.2 radians
      final rotationAngle = (random.nextDouble() - 0.5) * 0.4;
      
      // Random rotation duration between 3 and 8 seconds
      final rotationDuration = 3.0 + random.nextDouble() * 5.0;
      
      // Random delay
      final delay = random.nextDouble() * 3.0;
      
      Future.delayed(Duration(milliseconds: (delay * 1000).round()), () {
        if (isMounted) {
          add(
            RotateEffect.by(
              rotationAngle,
              EffectController(
                duration: rotationDuration,
                alternate: true,
                infinite: true,
                curve: Curves.easeInOut,
              ),
            ),
          );
        }
      });
    }
  }


  @override
  bool onTapDown(TapDownEvent event) {
    if (!isPopping) {
      pop(userTriggered: true);
    }
    return true;
  }

  void pop({bool userTriggered = false}) {
    if (isPopping) return;
    
    isPopping = true;
    poppedByUser = userTriggered;
    
    // Create exciting multi-stage pop animation
    
    // Stage 1: Quick expand (0.1s)
    final expandEffect = ScaleEffect.to(
      Vector2.all(1.8),
      EffectController(duration: 0.1, curve: Curves.easeOut),
    );
    
    // Stage 2: Slight compression (0.05s)
    final compressEffect = ScaleEffect.to(
      Vector2.all(0.8),
      EffectController(duration: 0.05, curve: Curves.easeIn),
    );
    
    // Stage 3: Final explosion (0.15s)
    final explodeEffect = ScaleEffect.to(
      Vector2.all(2.5),
      EffectController(duration: 0.15, curve: Curves.easeOut),
    );
    
    // Fade out effect
    final fadeEffect = OpacityEffect.to(
      0.0,
      EffectController(duration: 0.3, curve: Curves.easeIn),
    );
    
    // Rotation effect for more dynamic feel
    final rotateEffect = RotateEffect.by(
      0.5, // Half rotation
      EffectController(duration: 0.3),
    );
    
    // Chain the scale effects
    add(expandEffect);
    expandEffect.onComplete = () {
      if (isMounted) {
        add(compressEffect);
        compressEffect.onComplete = () {
          if (isMounted) {
            add(explodeEffect);
          }
        };
      }
    };
    
    add(fadeEffect);
    add(rotateEffect);
    
    // Create enhanced particle effects
    _createExcitingPopParticles();
    
    // Notify parent and remove bubble
    onPop?.call(this, userTriggered);
    
    Future.delayed(const Duration(milliseconds: 300), () {
      if (isMounted) {
        removeFromParent();
      }
    });
  }

  void _createExcitingPopParticles() {
    final random = Random();
    const particleCount = 4; // Further reduced for better performance
    
    // Use object pooling approach - create fewer, simpler particles
    for (int i = 0; i < particleCount; i++) {
      final angle = (i / particleCount) * 2 * pi;
      
      // Simplified particle creation
      final baseSpeed = 50 + random.nextDouble() * 30; // 50-80 speed
      final particleVelocity = Vector2(
        cos(angle) * baseSpeed,
        sin(angle) * baseSpeed,
      );
      
      // Fixed particle size for performance
      const particleSize = 3.0;
      
      // Simplified particle color
      final particleColor = bubbleColor.withValues(alpha: 0.8);
      
      final particle = CircleComponent(
        radius: particleSize,
        position: position.clone(),
        paint: Paint()
          ..color = particleColor
          ..style = PaintingStyle.fill,
      );
      
      parent?.add(particle);
      
      // Combine effects for better performance
      final combinedEffect = MoveEffect.by(
        particleVelocity + Vector2(0, 40), // Combined movement + gravity
        EffectController(duration: 0.3, curve: Curves.easeOut), // Faster animation
      );
      
      particle.add(combinedEffect);
      
      // Single fade effect instead of multiple effects
      particle.add(
        OpacityEffect.to(
          0.0,
          EffectController(duration: 0.3, curve: Curves.easeIn),
        ),
      );
      
      // Faster cleanup
      Future.delayed(const Duration(milliseconds: 300), () {
        if (particle.isMounted) {
          particle.removeFromParent();
        }
      });
    }
  }

  /// Add shake effect to this bubble (called when phone is shaken)
  void addShakeEffect() {
    if (isPopping) return;
    
    final random = Random();
    
    // Random shake direction
    final shakeDirection = Vector2(
      (random.nextDouble() - 0.5) * 60, // -30 to +30
      (random.nextDouble() - 0.5) * 60, // -30 to +30
    );
    
    // Add shake movement effect
    add(
      MoveEffect.by(
        shakeDirection,
        EffectController(
          duration: 0.4,
          alternate: true,
          curve: Curves.elasticOut,
        ),
      ),
    );
    
    // Add scale bounce effect
    add(
      ScaleEffect.by(
        Vector2.all(0.3),
        EffectController(
          duration: 0.2,
          alternate: true,
          curve: Curves.elasticOut,
        ),
      ),
    );
    
    // Add slight rotation effect
    add(
      RotateEffect.by(
        (random.nextDouble() - 0.5) * 0.4, // Random rotation
        EffectController(
          duration: 0.3,
          alternate: true,
          curve: Curves.elasticOut,
        ),
      ),
    );
  }

  // Collision detection methods
  @override
  bool onCollision(Set<Vector2> intersectionPoints, PositionComponent other) {
    super.onCollision(intersectionPoints, other);
    
    if (other is Bubble && !isPopping && !other.isPopping) {
      _handleBubbleCollision(other, intersectionPoints);
    }
    return true;
  }
  
  void _handleBubbleCollision(Bubble other, Set<Vector2> intersectionPoints) {
    if (intersectionPoints.isEmpty) return;
    
    // Calculate collision direction
    final direction = (position - other.position).normalized();
    
    // Apply collision response - bubbles bounce off each other
    final collisionForce = 30.0;
    velocity.add(direction * collisionForce);
    other.velocity.add(direction * -collisionForce);
    
    // Apply damping to prevent infinite acceleration
    velocity.scale(collisionDamping);
    other.velocity.scale(collisionDamping);
    
    // Add slight separation to prevent sticking
    final separation = direction * 2.0;
    position.add(separation);
    other.position.sub(separation);
  }
  
  /// Apply tilt force to bubble based on device orientation
  void applyTiltForce(double tiltStrength) {
    if (isPopping) return;
    
    // Apply force opposite to tilt direction
    // If device tilts right, bubbles move left and vice versa
    final tiltForce = -tiltStrength * 15.0; // Adjust force strength
    velocity.x += tiltForce;
    
    // Add slight vertical component for more natural movement
    velocity.y += tiltForce * 0.3;
  }
}