import 'package:flame/components.dart';
import 'package:flutter/material.dart';
import 'dart:math';

import 'package:shitman/game/components/base.dart';

class VisionCone extends ShitComponent {
  double direction; // Angle in radians
  final double range;
  final double fov; // Field of view angle in radians
  final Color color;
  double opacity;

  List<Vector2> visionVertices = [];
  late Paint fillPaint;
  late Paint strokePaint;

  VisionCone({
    required Vector2 origin,
    required this.direction,
    this.range = 150.0,
    this.fov = pi / 3, // 60 degrees
    this.color = Colors.red,
    this.opacity = 0.3,
  }) : super(position: origin);

  @override
  Future<void> onLoad() async {
    await super.onLoad();

    fillPaint =
        Paint()
          ..color = color.withValues(alpha: opacity)
          ..style = PaintingStyle.fill;

    strokePaint =
        Paint()
          ..color = color.withValues(alpha: opacity + 0.2)
          ..style = PaintingStyle.stroke
          ..strokeWidth = 2.0;

    _calculateVisionCone();
  }

  void updatePosition(Vector2 newOrigin, double newDirection) {
    position.setFrom(newOrigin);
    direction = newDirection;
    _calculateVisionCone();
  }

  void _calculateVisionCone() {
    visionVertices.clear();

    // Start from the origin (0,0 relative to this component)
    visionVertices.add(Vector2.zero());

    // Calculate the two edge rays of the vision cone
    final leftAngle = direction - fov / 2;
    final rightAngle = direction + fov / 2;

    // Create points along the vision cone arc
    const int arcPoints = 10;
    for (int i = 0; i <= arcPoints; i++) {
      final angle = leftAngle + (rightAngle - leftAngle) * (i / arcPoints);
      final point = Vector2(cos(angle), sin(angle)) * range;
      visionVertices.add(point);
    }

    // Close the cone back to origin
    visionVertices.add(Vector2.zero());
  }

  /// Check if a point is within the vision cone
  bool canSee(Vector2 point) {
    // Get the world position of this vision cone by adding parent position
    final parentPosition =
        (parent as PositionComponent?)?.position ?? Vector2.zero();
    final worldPosition = parentPosition + position;
    final toPoint = point - worldPosition;
    final distance = toPoint.length;

    // Check if within range
    if (distance > range) return false;

    // Check if within angle
    final angleToPoint = atan2(toPoint.y, toPoint.x);
    final angleDiff = _normalizeAngle(angleToPoint - direction);

    return angleDiff.abs() <= fov / 2;
  }

  /// Normalize angle to [-pi, pi]
  double _normalizeAngle(double angle) {
    while (angle > pi) {
      angle -= 2 * pi;
    }
    while (angle < -pi) {
      angle += 2 * pi;
    }
    return angle;
  }

  /// Raycast from origin to target, checking for obstructions
  bool hasLineOfSight(Vector2 target, List<Component> obstacles) {
    // Simple line-of-sight check - can be enhanced with proper raycasting
    final parentPosition =
        (parent as PositionComponent?)?.position ?? Vector2.zero();
    final worldPosition = parentPosition + position;
    final rayDirection = target - worldPosition;
    final distance = rayDirection.length;
    final normalizedDir = rayDirection.normalized();

    // Check points along the ray for obstacles
    const double stepSize = 5.0;
    for (double step = stepSize; step < distance; step += stepSize) {
      final checkPoint = worldPosition + normalizedDir * step;

      // Check if this point intersects with any obstacles
      for (final obstacle in obstacles) {
        if (obstacle is RectangleComponent) {
          if (obstacle.containsPoint(checkPoint)) {
            return false; // Line of sight blocked
          }
        }
      }
    }

    return true; // Clear line of sight
  }

  void updateOpacity(double newOpacity) {
    opacity = newOpacity;
    fillPaint =
        Paint()
          ..color = color.withValues(alpha: opacity)
          ..style = PaintingStyle.fill;

    strokePaint =
        Paint()
          ..color = color.withValues(alpha: opacity + 0.2)
          ..style = PaintingStyle.stroke
          ..strokeWidth = 2.0;
  }

  @override
  void render(Canvas canvas) {
    if (visionVertices.length < 3 || opacity <= 0.0) return;

    // Create path for vision cone
    final path = Path();
    path.moveTo(visionVertices[0].x, visionVertices[0].y);

    for (int i = 1; i < visionVertices.length; i++) {
      path.lineTo(visionVertices[i].x, visionVertices[i].y);
    }
    path.close();

    // Draw filled vision cone
    canvas.drawPath(path, fillPaint);

    // Draw vision cone outline
    canvas.drawPath(path, strokePaint);
  }

  @override
  Future<void> reset() async {
    // Reset vision cone state
    direction = 0.0;
    opacity = 0.3;
    visionVertices.clear();
    _calculateVisionCone();
    appLog.finest('Vision cone reset');
  }
}
