import 'dart:async';
import 'package:flutter/foundation.dart';
import '../models/route_model.dart';
import '../models/models.dart';
import '../data/routes_data.dart';
import '../database/db_helper.dart';

enum NotifEvent {
  routeStart, truckProximity, truckApproaching15min, routeCompleted,
  gpsLost, truckStopped, routeCancelled, reviewPrompt, none
}

class AppNotification {
  final NotifEvent event;
  final String title;
  final String body;
  final String routeId;
  final DateTime timestamp;
  AppNotification({required this.event, required this.title,
      required this.body, required this.routeId})
      : timestamp = DateTime.now();
}

class SimulatorState {
  final String routeId;
  int positionIndex;
  bool gpsActive;
  DateTime lastMoved;
  bool stoppedAlertSent;
  bool reviewPromptSent;
  SimulatorState({required this.routeId, this.positionIndex=0,
      this.gpsActive=true, required this.lastMoved,
      this.stoppedAlertSent=false, this.reviewPromptSent=false});
}

class RouteSimulatorService extends ChangeNotifier {
  final Map<String, SimulatorState> _states = {};
  Timer? _globalTimer;
  Timer? _gpsMonitorTimer;

  AppNotification? _lastNotification;
  final List<AppNotification> _history = [];

  // ── Getters ──────────────────────────────────────────────────────────────
  AppNotification? get lastNotification => _lastNotification;

  // Ciudadano/Conductor: solo su ruta
  AppNotification? getNotificationForRoute(String routeId) {
    if (_lastNotification?.routeId == routeId) return _lastNotification;
    return null;
  }

  List<AppNotification> get history => List.unmodifiable(_history);
  List<AppNotification> historyForRoute(String routeId) =>
      _history.where((n) => n.routeId == routeId).toList();

  bool needsReviewPrompt(String routeId) =>
      _states[routeId]?.reviewPromptSent == true;

  // ── Inicio ───────────────────────────────────────────────────────────────
  void startAllRoutes() {
    for (final r in routesData) {
      _states[r.routeId] = SimulatorState(routeId:r.routeId, lastMoved:DateTime.now());
    }
    _globalTimer?.cancel();
    _globalTimer = Timer.periodic(const Duration(seconds:30), (_) => _tick());
    _gpsMonitorTimer?.cancel();
    _gpsMonitorTimer = Timer.periodic(const Duration(minutes:5), (_) => _monitorGps());
    notifyListeners();
  }

  void startRoute(String routeId) {
    _states[routeId] = SimulatorState(routeId:routeId, lastMoved:DateTime.now());
    _globalTimer ??= Timer.periodic(const Duration(seconds:30), (_) => _tick());
    notifyListeners();
  }

  void _tick() {
    bool changed = false;
    for (final entry in _states.entries) {
      final state = entry.value;
      final route = getRouteById(state.routeId);
      if (route == null || !state.gpsActive) continue;
      if (state.positionIndex < route.positions.length - 1) {
        state.positionIndex++;
        state.lastMoved = DateTime.now();
        _checkNotification(state, route);
        changed = true;
      }
    }
    if (changed) notifyListeners();
  }

  void _monitorGps() {
    for (final state in _states.values) {
      if (!state.gpsActive) continue;
      final diff = DateTime.now().difference(state.lastMoved);
      if (diff.inMinutes >= 30 && !state.stoppedAlertSent) {
        state.stoppedAlertSent = true;
        _fireAndSave(event:NotifEvent.truckStopped, routeId:state.routeId,
          title:'⚠️ Camión detenido',
          body:'El camión ${state.routeId} lleva +30 min sin moverse. Verifica.',
          tipo:'CAMION_DETENIDO');
      }
    }
  }

  void _checkNotification(SimulatorState state, RouteModel route) {
    final idx = state.positionIndex;
    final total = route.positions.length;

    if (idx == 1) {
      // Ruta iniciada
      _fireNotif(NotifEvent.routeStart, '¡Ruta Iniciada! 🚛',
          'El camión ha salido del Relleno Sanitario rumbo a tu sector. '
          'Prepara tus bolsas pero espera la señal para sacarlas.', state.routeId);
    } else if (idx == 2) {
      // ~30 min — aviso preventivo
      _fireNotif(NotifEvent.truckApproaching15min, '🕐 El camión se acerca',
          'Tu camión recolector está en camino. Tendrás otro aviso cuando esté a '
          '15 minutos. ⚠️ No saques la basura todavía — espera el aviso.', state.routeId);
    } else if (idx == 3) {
      // ~15 min — MOMENTO de sacar la basura
      _fireNotif(NotifEvent.truckProximity, '⚠️ ¡Saca tus bolsas AHORA!',
          'El camión llega en aprox. 15 minutos a tu colonia. '
          'Este es el momento de sacar tus bolsas a la acera. '
          '🚫 No persigas ni interceptes la unidad.', state.routeId);
    } else if (idx == total - 2) {
      // Pasando por la zona
      _fireNotif(NotifEvent.truckProximity, '✅ El camión está en tu zona',
          'El camión recolector está pasando por tu colonia. '
          'Si ya sacaste tus bolsas, el servicio está en curso.', state.routeId);
    } else if (idx == total - 1) {
      // Servicio finalizado → prompt de reseña
      state.reviewPromptSent = true;
      _fireNotif(NotifEvent.reviewPrompt, '🌟 ¿Cómo fue el servicio?',
          '¡El camión concluyó su jornada! Ayúdanos calificando el servicio '
          'de recolección de hoy. Tu opinión mejora el servicio.', state.routeId);
    }
  }

  void _fireNotif(NotifEvent event, String title, String body, String routeId) {
    final n = AppNotification(event:event, title:title, body:body, routeId:routeId);
    _lastNotification = n;
    _history.insert(0, n);
    notifyListeners();
  }

  Future<void> _fireAndSave({required NotifEvent event, required String routeId,
      required String title, required String body, required String tipo}) async {
    _fireNotif(event, title, body, routeId);
    await DbHelper.insertAlerta(AlertaModel(tipo:tipo, routeId:routeId,
        mensaje:body, fecha:DateTime.now().toIso8601String()));
  }

  void fireCustomNotification(String title, String body, String routeId, NotifEvent event) {
    _fireNotif(event, title, body, routeId);
  }

  // ── GPS ──────────────────────────────────────────────────────────────────
  Future<void> simulateGpsLost(String routeId) async {
    final state = _states[routeId];
    if (state == null) return;
    state.gpsActive = false;
    await _fireAndSave(event:NotifEvent.gpsLost, routeId:routeId,
        title:'📡 GPS Desactivado',
        body:'Se perdió la señal GPS del camión $routeId.',
        tipo:'GPS_PERDIDO');
    notifyListeners();
  }

  void restoreGps(String routeId) {
    final state = _states[routeId];
    if (state == null) return;
    state.gpsActive = true;
    state.stoppedAlertSent = false;
    notifyListeners();
  }

  // ── Getters de estado ────────────────────────────────────────────────────
  SimulatorState? getState(String routeId) => _states[routeId];
  int getPositionIndex(String routeId) => _states[routeId]?.positionIndex ?? 0;
  bool isTruckClose(String routeId) => getPositionIndex(routeId) >= 3;
  bool isRouteCompleted(String routeId) {
    final state = _states[routeId];
    if (state == null) return false;
    final route = getRouteById(routeId);
    if (route == null) return false;
    return state.positionIndex >= route.positions.length - 1;
  }
  bool isGpsActive(String routeId) => _states[routeId]?.gpsActive ?? true;

  String getEtaText(String routeId) {
    final state = _states[routeId];
    if (state == null) return 'Sin información';
    if (!state.gpsActive) return '📡 Señal GPS perdida';
    final route = getRouteById(routeId);
    if (route == null) return 'Ruta no encontrada';
    final idx = state.positionIndex;
    if (idx >= route.positions.length) return '✅ Servicio finalizado';
    switch (idx) {
      case 0: return '🕐 Ruta por iniciar';
      case 1: return '🚛 Camión en camino — mantén tus bolsas adentro';
      case 2: return '🚛 Aprox. 30 min — espera el aviso de 15 min';
      case 3: return '⚠️ ¡15 min! Saca tus bolsas a la acera ahora';
      case 4: return '🔔 El camión está en tu colonia';
      case 5: return '✅ Recogiendo basura en tu zona';
      case 6: return '↩️ Regresando al relleno sanitario';
      default: return '🏁 Servicio del día finalizado';
    }
  }

  void dismissNotification() { _lastNotification = null; notifyListeners(); }
  void dismissRouteNotification(String routeId) {
    if (_lastNotification?.routeId == routeId) {
      _lastNotification = null;
      notifyListeners();
    }
  }
  void clearReviewPrompt(String routeId) {
    final state = _states[routeId];
    if (state != null) state.reviewPromptSent = false;
    notifyListeners();
  }

  @override
  void dispose() {
    _globalTimer?.cancel();
    _gpsMonitorTimer?.cancel();
    super.dispose();
  }
}
