import 'dart:async';
import 'dart:math' show cos, pi, sin;
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intaleq_maps/intaleq_maps.dart';

import '../../../print.dart';
import 'map_engine_controller.dart';
import 'map_socket_controller.dart';
import 'nearby_drivers_controller.dart';
import 'ride_lifecycle_controller.dart';
import 'ride_state.dart';
import 'location_search_controller.dart';

class RideSimulationController extends GetxController {
  Timer? _simulationTimer;
  int _step = 0;
  bool _isSimulating = false;

  List<LatLng> _simulatedRoute = [];
  int _driverRouteIndex = 0;

  String get simulationStatus => _isSimulating
      ? 'Simulating... Step $_step'
      : 'Idle';

  // آمود نقاط المسار (Jordan area: Amman center ~ 31.95, 35.91)
  static const double _centerLat = 31.95;
  static const double _centerLng = 35.91;
  static const double _routeDelta = 0.03;
  static const int _routePointCount = 100;

  void _generateRoutePoints() {
    _simulatedRoute = [];
    for (int i = 0; i < _routePointCount; i++) {
      double fraction = i / (_routePointCount - 1);
      double lat = _centerLat + fraction * _routeDelta;
      double lng = _centerLng + fraction * _routeDelta +
          0.005 * sin(fraction * pi * 2);
      _simulatedRoute.add(LatLng(lat, lng));
    }
  }

  void startFullSimulation() {
    if (_isSimulating) return;
    _isSimulating = true;
    _step = 0;
    _driverRouteIndex = 0;
    _generateRoutePoints();

    Log.print('=' * 70);
    Log.print('🚀 RIDE SIMULATION STARTED — Full lifecycle test');
    Log.print('=' * 70);

    _simulationTimer = Timer.periodic(const Duration(seconds: 2), (timer) {
      switch (_step) {
        case 0:
          _simulateSearchingState();
          break;
        case 1:
          _simulateDriverAppliedState();
          break;
        case 2:
          _simulateDriverEnRoute(isFirst: true);
          break;
        case 3:
          _simulateDriverEnRoute(isFirst: false);
          break;
        case 4:
          _simulateDriverArrivedState();
          break;
        case 5:
          _simulateRideBeginState();
          break;
        case 6:
          _simulateRideInProgress();
          break;
        case 7:
          _simulateRideFinishedState();
          timer.cancel();
          _isSimulating = false;
          Log.print('=' * 70);
          Log.print('✅ RIDE SIMULATION COMPLETED');
          Log.print('=' * 70);
          break;
      }
      _step++;
      update();
    });
  }

  void stopSimulation() {
    _simulationTimer?.cancel();
    _simulationTimer = null;
    _isSimulating = false;
    _step = 0;
    _driverRouteIndex = 0;
    Log.print('🛑 Simulation stopped');
    update();
  }

  void _logPolylineState(String stage) {
    final mapEngine = Get.find<MapEngineController>();
    Log.print('');
    Log.print('╔══ Polyline State [$stage] ══╗');
    if (mapEngine.polyLines.isEmpty) {
      Log.print('  ║ (no polylines)');
    }
    for (final p in mapEngine.polyLines) {
      String colorStr;
      if (p.color == Colors.amber) {
        colorStr = 'AMBER';
      } else if (p.color.value == 0xFF2196F3) {
        colorStr = 'BLUE';
      } else if (p.color == Colors.blueGrey) {
        colorStr = 'BLUE_GREY';
      } else {
        colorStr = '#${p.color.value.toRadixString(16).padLeft(8, '0')}';
      }
      Log.print('  ║ • ${p.polylineId.value}: ${p.points.length} pts, '
          'color: $colorStr, w: ${p.width}');
    }
    Log.print('╠══ Markers [$stage] ══╣');
    if (mapEngine.markers.isEmpty) {
      Log.print('  ║ (no markers)');
    }
    for (final m in mapEngine.markers) {
      Log.print('  ║ • ${m.markerId.value}: '
          '(${m.position.latitude.toStringAsFixed(5)}, '
          '${m.position.longitude.toStringAsFixed(5)}), '
          'heading: ${m.rotation.toStringAsFixed(1)}');
    }
    Log.print('╚══════════════════════╝');
    Log.print('');
  }

  void _verifyPolylineCondition(bool condition, String description) {
    if (!condition) {
      Log.print('  ❌ VERIFY FAILED: $description');
    } else {
      Log.print('  ✅ VERIFY PASSED: $description');
    }
  }

  LatLng _interpolateRoutePoint(double fraction) {
    if (_simulatedRoute.isEmpty) return LatLng(_centerLat, _centerLng);
    int total = _simulatedRoute.length;
    double exactIdx = fraction * (total - 1);
    int idx = exactIdx.floor();
    if (idx >= total - 1) return _simulatedRoute.last;
    double t = exactIdx - idx;
    return LatLng(
      _simulatedRoute[idx].latitude +
          t * (_simulatedRoute[idx + 1].latitude - _simulatedRoute[idx].latitude),
      _simulatedRoute[idx].longitude +
          t * (_simulatedRoute[idx + 1].longitude - _simulatedRoute[idx].longitude),
    );
  }

  void _simulateSearchingState() {
    Log.print('🟡 [Step $_step] STATE: SEARCHING');
    final ride = Get.find<RideLifecycleController>();
    final locSearch = Get.find<LocationSearchController>();

    ride.currentRideState.value = RideState.searching;
    ride.rideId = 'sim_ride_001';
    ride.isSearchingWindow = true;

    // آمود موقع الراكب
    LatLng passengerLoc = _simulatedRoute.isNotEmpty
        ? _simulatedRoute.first
        : LatLng(_centerLat, _centerLng);
    locSearch.passengerLocation = passengerLoc;
    ride.passengerLocation = passengerLoc;
    ride.update();

    _logPolylineState('searching');
    _verifyPolylineCondition(
        Get.find<MapEngineController>().polyLines.isEmpty,
        'No route polylines during searching');
    Log.print('');
  }

  void _simulateDriverAppliedState() {
    Log.print('🟢 [Step $_step] STATE: DRIVER APPLIED');
    final ride = Get.find<RideLifecycleController>();
    final nearbyDrivers = Get.find<NearbyDriversController>();
    final locSearch = Get.find<LocationSearchController>();

    ride.currentRideState.value = RideState.driverApplied;
    ride.statusRide = 'Apply';
    ride.isSearchingWindow = false;

    // آمود معلومات السائق
    ride.driverId = 'sim_driver_001';
    ride.driverName = 'Simulated Captain';
    ride.make = 'Toyota';
    ride.model = 'Corolla';
    ride.carColor = 'White';
    ride.licensePlate = 'SIM-123';
    ride.driverRate = '4.8';
    ride.driverPhone = '+9627XXXXXXXX';
    ride.driverToken = 'sim_token_001';

    // آمود مواقع البداية والنهاية
    ride.passengerLocation = _simulatedRoute.first;
    ride.myDestination = _simulatedRoute.last;
    locSearch.passengerLocation = _simulatedRoute.first;
    locSearch.myDestination = _simulatedRoute.last;

    // محاكاة موقع السائق (45% على طول المسار)
    LatLng driverPos = _interpolateRoutePoint(0.45);
    nearbyDrivers.driverCarsLocationToPassengerAfterApplied = [driverPos];

    // رسم مسار السائق إلى الراكب
    ride.calculateDriverToPassengerRoute(driverPos, ride.passengerLocation);

    // وضع ماركر السائق على الخريطة
    ride.updateDriverMarker(driverPos, 45.0);
    ride.update();

    _logPolylineState('driver_applied');

    // التحقق من وجود الخطوط المطلوبة
    final polylines = Get.find<MapEngineController>().polyLines;
    _verifyPolylineCondition(
      polylines.any((p) => p.polylineId.value == 'driver_route_solid'),
      'driver_route_solid (amber solid line) exists');
    _verifyPolylineCondition(
      polylines.any((p) => p.polylineId.value.startsWith('passenger_walk_line')),
      'passenger_walk_line (dashed walk line) exists');
    _verifyPolylineCondition(
      polylines.any((p) => p.color == Colors.amber),
      'Amber-colored polyline exists (driver route)');
    _verifyPolylineCondition(
      polylines.any((p) => p.color == Colors.blueGrey),
      'BlueGrey-colored polyline exists (walk dashes)');
    Log.print('');
  }

  void _simulateDriverEnRoute({required bool isFirst}) {
    String label = isFirst ? '1' : '2';
    Log.print('🔵 [Step $_step] STATE: DRIVER APPROACHING (update #$label)');
    final ride = Get.find<RideLifecycleController>();
    final nearbyDrivers = Get.find<NearbyDriversController>();

    _driverRouteIndex += 18;
    if (_driverRouteIndex > _routePointCount - 1) {
      _driverRouteIndex = _routePointCount - 1;
    }

    LatLng newPos = _simulatedRoute[_driverRouteIndex];

    // تحديث موقع السائق
    nearbyDrivers.driverCarsLocationToPassengerAfterApplied = [newPos];

    ride.updateDriverMarker(newPos, 50.0);
    ride.updateRemainingRoute(newPos);
    ride.update();

    _logPolylineState('driver_en_route_$label');

    // التحقق: driver_route_solid يبقى موجود ويتقلص
    final polylines = Get.find<MapEngineController>().polyLines;
    _verifyPolylineCondition(
      polylines.any((p) => p.polylineId.value == 'driver_route_solid'),
      'driver_route_solid still exists (shrinking)');
    _verifyPolylineCondition(
      polylines.any((p) => p.polylineId.value.startsWith('passenger_walk_line')),
      'passenger_walk_line still exists');
    Log.print('');
  }

  void _simulateDriverArrivedState() {
    Log.print('🟣 [Step $_step] STATE: DRIVER ARRIVED');
    final ride = Get.find<RideLifecycleController>();

    ride.currentRideState.value = RideState.driverArrived;
    ride.statusRide = 'Arrived';

    // محاكاة وصول السائق: تحديد الموقع كآخر نقطة طريق
    LatLng driverPos = _simulatedRoute.first;

    // تطبيق arrived logic
    ride.polyLines = ride.polyLines
        .where((p) =>
            p.polylineId.value != 'main_route' &&
            p.polylineId.value != 'route_direct' &&
            !p.polylineId.value.startsWith('driver_route'))
        .toSet();

    // رسم المسار الجديد (من السائق إلى الوجهة النهائية)
    ride.calculateDriverToPassengerRoute(driverPos, ride.myDestination,
        isBeginPhase: true);

    ride.update();

    _logPolylineState('driver_arrived');

    final polylines = Get.find<MapEngineController>().polyLines;
    // في حالة الوصول: يجب أن يظهر main_route (أزرق) بدلاً من driver_route_solid
    _verifyPolylineCondition(
      !polylines.any((p) => p.polylineId.value == 'driver_route_solid'),
      'driver_route_solid (amber) has been cleared');
    _verifyPolylineCondition(
      polylines.any((p) => p.polylineId.value == 'main_route'),
      'main_route (blue) has been drawn to destination');
    Log.print('');
  }

  void _simulateRideBeginState() {
    Log.print('🔴 [Step $_step] STATE: RIDE IN PROGRESS (Begin)');
    final ride = Get.find<RideLifecycleController>();

    ride.currentRideState.value = RideState.inProgress;
    ride.statusRide = 'Begin';

    LatLng driverPos = _simulatedRoute.first;

    // محاكاة processRideBegin: مسح الخطوط القديمة ورسم الخط الجديد
    ride.polyLines = ride.polyLines
        .where((p) =>
            p.polylineId.value != 'main_route' &&
            p.polylineId.value != 'route_direct' &&
            !p.polylineId.value.startsWith('driver_route'))
        .toSet();

    ride.calculateDriverToPassengerRoute(driverPos, ride.myDestination,
        isBeginPhase: true);

    ride.rideIsBeginPassengerTimer();
    ride.update();

    _logPolylineState('ride_begin');

    final polylines = Get.find<MapEngineController>().polyLines;
    _verifyPolylineCondition(
      polylines.any((p) => p.polylineId.value == 'main_route'),
      'main_route (blue) exists for trip');
    _verifyPolylineCondition(
      !polylines.any((p) => p.polylineId.value.startsWith('driver_route')),
      'No driver_route* polylines remain');
    _verifyPolylineCondition(
      polylines.any((p) => p.color.value == 0xFF2196F3),
      'Blue polyline (main route) is present');
    Log.print('');
  }

  void _simulateRideInProgress() {
    Log.print('📱 [Step $_step] STATE: CAR MOVING (remaining route update)');
    final ride = Get.find<RideLifecycleController>();
    final nearbyDrivers = Get.find<NearbyDriversController>();

    _driverRouteIndex += 30;
    if (_driverRouteIndex > _routePointCount - 1) {
      _driverRouteIndex = _routePointCount - 1;
    }

    LatLng newPos = _simulatedRoute[_driverRouteIndex];

    nearbyDrivers.driverCarsLocationToPassengerAfterApplied = [newPos];

    ride.updateDriverMarker(newPos, 70.0);
    ride.updateRemainingRoute(newPos, updateEta: true);
    ride.update();

    _logPolylineState('ride_in_progress');

    final polylines = Get.find<MapEngineController>().polyLines;
    _verifyPolylineCondition(
      polylines.any((p) => p.polylineId.value == 'main_route'),
      'main_route (blue) exists with remaining points');
    _verifyPolylineCondition(
      !polylines.any((p) => p.polylineId.value.startsWith('driver_route')),
      'No driver_route* polylines remain');
    Log.print('');
  }

  void _simulateRideFinishedState() {
    Log.print('🏁 [Step $_step] STATE: RIDE FINISHED');
    final ride = Get.find<RideLifecycleController>();
    final mapEngine = Get.find<MapEngineController>();

    ride.currentRideState.value = RideState.finished;
    ride.statusRide = 'Finished';
    ride.isSearchingWindow = false;

    ride.stopAllTimers();
    mapEngine.clearPolyline();
    mapEngine.markers = {};
    ride.update();

    _logPolylineState('ride_finished');

    _verifyPolylineCondition(
      Get.find<MapEngineController>().polyLines.isEmpty,
      'All polylines cleared');
    _verifyPolylineCondition(
      Get.find<MapEngineController>().markers.isEmpty,
      'All markers removed');
    Log.print('');
  }

  // Simulate a single WebSocket location update event
  void simulateSocketLocationUpdate(LatLng position, double heading) {
    final ride = Get.find<RideLifecycleController>();
    final nearbyDrivers = Get.find<NearbyDriversController>();
    final mapSocket = Get.find<MapSocketController>();

    Log.print('📡 Simulating socket location update: '
        '(${position.latitude.toStringAsFixed(5)}, ${position.longitude.toStringAsFixed(5)})');

    nearbyDrivers.driverCarsLocationToPassengerAfterApplied = [position];
    ride.driverCarsLocationToPassengerAfterApplied = [position];

    ride.checkAndRecalculateIfDeviated(
      position,
      heading: heading,
      speed: 30.0,
    );

    final mapEngine = Get.find<MapEngineController>();
    if (mapEngine.mapController != null) {
      mapEngine.mapController!
          .animateCamera(CameraUpdate.newLatLngZoom(position, 16.5));
    }

    ride.updateDriverMarker(position, heading);
    ride.updateRemainingRoute(position);
    ride.update();
  }

  @override
  void onClose() {
    _simulationTimer?.cancel();
    super.onClose();
  }
}
