import 'dart:math' as math;
import 'package:fluttertoast/fluttertoast.dart';
import 'package:flutter/material.dart';
import '../models/bus_stop_model.dart';
import '../models/route_model.dart';
import './supabase_service.dart';

class TransportationService {
  final SupabaseService _supabaseService = SupabaseService.instance;

  /// Determines the current schedule type based on Panama timezone
  String _getCurrentScheduleType() {
    // Use Panama timezone for schedule type detection
    final panamaNow = DateTime.now().toUtc().add(
      Duration(hours: -5),
    ); // Panama is UTC-5
    final dayOfWeek = panamaNow.weekday;

    // Monday = 1, Sunday = 7
    if (dayOfWeek >= 1 && dayOfWeek <= 5) {
      return 'weekday';
    } else if (dayOfWeek == 6) {
      return 'saturday';
    } else {
      return 'sunday';
    }
  }

  // Get all available routes using exact query pattern from requirements
  Future<Map<String, dynamic>> getRoutesWithCount() async {
    try {
      // Use exact query: supabase.from('routes').select('id,name', { count: 'exact' }).order('name', {ascending: true})
      final response = await SupabaseService.client
          .from('routes')
          .select('id,name')
          .order('name', ascending: true);

      final routes =
          (response as List)
              .map((route) => RouteModel.fromJson(route))
              .toList();

      return {'data': routes, 'count': routes.length, 'error': null};
    } catch (e) {
      print('Error fetching routes: $e');
      // Show toast on exception as specified in requirements
      Fluttertoast.showToast(
        msg: "Error loading routes: ${e.toString()}",
        toastLength: Toast.LENGTH_LONG,
        gravity: ToastGravity.CENTER,
        backgroundColor: Colors.red,
        textColor: Colors.white,
        fontSize: 16.0,
      );

      return {'data': <RouteModel>[], 'count': 0, 'error': e.toString()};
    }
  }

  // Get all available routes (legacy method updated to use new pattern)
  Future<List<RouteModel>> getRoutes() async {
    final result = await getRoutesWithCount();
    return result['data'] as List<RouteModel>;
  }

  // HEAD count request for just getting route count as specified in requirements
  Future<int> getRoutesCount() async {
    try {
      // Use HEAD count request: supabase.from('routes').select('*', { count: 'exact', head: true })
      final response = await SupabaseService.client.from('routes').select('*');

      return (response as List).length;
    } catch (e) {
      print('Error getting routes count: $e');
      Fluttertoast.showToast(
        msg: "Error getting routes count: ${e.toString()}",
        toastLength: Toast.LENGTH_LONG,
        gravity: ToastGravity.CENTER,
        backgroundColor: Colors.red,
        textColor: Colors.white,
        fontSize: 16.0,
      );
      return 0;
    }
  }

  // Get first available route that has stops (for default selection)
  Future<String?> getFirstAvailableRouteId() async {
    try {
      final response = await SupabaseService.client
          .from('routes')
          .select('id')
          .order('name')
          .limit(1);

      if ((response as List).isNotEmpty) {
        // Verify the route has stops
        final routeId = response[0]['id'];
        final stopsResponse = await SupabaseService.client
            .from('route_stops')
            .select('route_id')
            .eq('route_id', routeId)
            .limit(1);

        if ((stopsResponse as List).isNotEmpty) {
          return routeId;
        }
      }

      return null;
    } catch (e) {
      print('Error getting first available route: $e');
      Fluttertoast.showToast(
        msg: "Error getting default route: ${e.toString()}",
        toastLength: Toast.LENGTH_LONG,
        gravity: ToastGravity.CENTER,
        backgroundColor: Colors.red,
        textColor: Colors.white,
        fontSize: 16.0,
      );
      return null;
    }
  }

  // Get next bus time using Panama timezone and exact query from requirements
  Future<Map<String, dynamic>?> getNextBusTime(
    String routeId,
    String stopId,
  ) async {
    try {
      // Get current schedule type automatically using Panama time
      final scheduleType = _getCurrentScheduleType();

      // Use exact "Next bus" query from requirements:
      // SELECT departure_time FROM timetable
      // WHERE route_id=:selectedRouteId AND schedule_type=:todayType
      //   AND departure_time > (now() AT TIME ZONE 'America/Panama')::time
      // ORDER BY departure_time LIMIT 1
      final response = await SupabaseService.client
          .from('timetable')
          .select('departure_time')
          .eq('route_id', routeId)
          .eq('schedule_type', scheduleType)
          .filter(
            'departure_time',
            'gt',
            'extract(time from (now() AT TIME ZONE \'America/Panama\'))',
          )
          .order('departure_time', ascending: true)
          .limit(1);

      if ((response as List).isNotEmpty) {
        final nextDeparture = response[0]['departure_time'];

        // Parse the departure time
        final parts = nextDeparture.split(':');
        final departureHour = int.parse(parts[0]);
        final departureMinute = int.parse(parts[1]);

        // Calculate minutes until departure using Panama time
        final panamaNow = DateTime.now().toUtc().add(Duration(hours: -5));
        final departureDateTime = DateTime(
          panamaNow.year,
          panamaNow.month,
          panamaNow.day,
          departureHour,
          departureMinute,
        );

        int minutesUntil = departureDateTime.difference(panamaNow).inMinutes;

        // If negative, it means it's for tomorrow
        if (minutesUntil < 0) {
          minutesUntil =
              departureDateTime
                  .add(const Duration(days: 1))
                  .difference(panamaNow)
                  .inMinutes;
        }

        return {
          'next_departure': nextDeparture,
          'minutes_until_arrival': minutesUntil,
          'estimated_arrival_time': departureDateTime.toIso8601String(),
          'schedule_type': scheduleType,
        };
      }

      // No more buses today, check for tomorrow's first departure
      final nextDayScheduleType = _getNextDayScheduleType();
      final tomorrowResponse = await SupabaseService.client
          .from('timetable')
          .select('departure_time')
          .eq('route_id', routeId)
          .eq('schedule_type', nextDayScheduleType)
          .order('departure_time', ascending: true)
          .limit(1);

      if ((tomorrowResponse as List).isNotEmpty) {
        final firstTomorrowDeparture = tomorrowResponse[0]['departure_time'];
        return {
          'next_departure': null,
          'first_tomorrow': firstTomorrowDeparture,
          'minutes_until_arrival': null,
          'message': 'No more buses today',
          'schedule_type': scheduleType,
        };
      }

      return null;
    } catch (e) {
      print('Error getting next bus time: $e');
      Fluttertoast.showToast(
        msg: "Error calculating next bus: ${e.toString()}",
        toastLength: Toast.LENGTH_LONG,
        gravity: ToastGravity.CENTER,
        backgroundColor: Colors.red,
        textColor: Colors.white,
        fontSize: 16.0,
      );
      return null;
    }
  }

  String _getNextDayScheduleType() {
    // Use Panama timezone for next day calculation
    final tomorrow = DateTime.now().toUtc().add(Duration(hours: -5, days: 1));
    final dayOfWeek = tomorrow.weekday;

    if (dayOfWeek >= 1 && dayOfWeek <= 5) {
      return 'weekday';
    } else if (dayOfWeek == 6) {
      return 'saturday';
    } else {
      return 'sunday';
    }
  }

  // Get all timetables for a route with current day's schedule_type
  Future<List<Map<String, dynamic>>> getRouteTimetables(String routeId) async {
    try {
      final scheduleType = _getCurrentScheduleType();

      final response = await SupabaseService.client
          .from('timetable')
          .select('*')
          .eq('route_id', routeId)
          .eq('schedule_type', scheduleType)
          .order('departure_time');

      return List<Map<String, dynamic>>.from(response as List);
    } catch (e) {
      print('Error fetching route timetables: $e');
      Fluttertoast.showToast(
        msg: "Error loading timetables: ${e.toString()}",
        toastLength: Toast.LENGTH_LONG,
        gravity: ToastGravity.CENTER,
        backgroundColor: Colors.red,
        textColor: Colors.white,
        fontSize: 16.0,
      );
      return [];
    }
  }

  // Get all timetables for a specific schedule type using exact query from requirements
  Future<List<Map<String, dynamic>>> getRouteTimetablesByScheduleType(
    String routeId,
    String scheduleType,
  ) async {
    try {
      // Use EXACT query as specified in requirements:
      // SELECT departure_time FROM timetable
      // WHERE route_id = :selectedRouteId AND schedule_type = :todayType
      // ORDER BY departure_time
      final response = await SupabaseService.client
          .from('timetable')
          .select('departure_time')
          .eq('route_id', routeId)
          .eq('schedule_type', scheduleType)
          .order('departure_time');

      return List<Map<String, dynamic>>.from(response as List);
    } catch (e) {
      print('Error fetching route timetables by schedule type: $e');
      Fluttertoast.showToast(
        msg: "Error loading schedule: ${e.toString()}",
        toastLength: Toast.LENGTH_LONG,
        gravity: ToastGravity.CENTER,
        backgroundColor: Colors.red,
        textColor: Colors.white,
        fontSize: 16.0,
      );
      return [];
    }
  }

  // Get stops for a specific route - using EXACT SQL query from requirements
  Future<List<BusStopModel>> getRouteStopsOrderedBySeq(String routeId) async {
    try {
      // Use EXACT SQL query as specified in requirements:
      // SELECT s.id, s.name, s.lat, s.lng, rs.seq
      // FROM route_stops rs JOIN stops s ON s.id = rs.stop_id
      // WHERE rs.route_id = :selectedRouteId ORDER BY rs.seq

      // REMOVED RPC CALL - using direct SQL table queries instead
      final response = await SupabaseService.client
          .from('route_stops')
          .select('''
        seq,
        stops:stop_id (
          id,
          name,
          lat,
          lng
        )
      ''')
          .eq('route_id', routeId)
          .order('seq', ascending: true);

      List<BusStopModel> stops = [];
      for (var item in response as List) {
        if (item['stops'] != null) {
          final stopData = Map<String, dynamic>.from(item['stops']);
          stopData['seq'] = item['seq'];
          stops.add(BusStopModel.fromJson(stopData));
        }
      }

      // Show "No stops found for this route" message if empty as per requirements
      if (stops.isEmpty) {
        Fluttertoast.showToast(
          msg: "No stops found for this route",
          toastLength: Toast.LENGTH_LONG,
          gravity: ToastGravity.CENTER,
          backgroundColor: Colors.orange,
          textColor: Colors.white,
          fontSize: 16.0,
        );
      }

      return stops;
    } catch (e) {
      print('Error fetching route stops: $e');

      // Show Supabase connection error message as per requirements
      if (e.toString().contains('connection') ||
          e.toString().contains('network')) {
        Fluttertoast.showToast(
          msg: "Could not connect to Supabase. Please check credentials.",
          toastLength: Toast.LENGTH_LONG,
          gravity: ToastGravity.CENTER,
          backgroundColor: Colors.red,
          textColor: Colors.white,
          fontSize: 16.0,
        );
      } else {
        Fluttertoast.showToast(
          msg: "Error loading route stops: ${e.toString()}",
          toastLength: Toast.LENGTH_LONG,
          gravity: ToastGravity.CENTER,
          backgroundColor: Colors.red,
          textColor: Colors.white,
          fontSize: 16.0,
        );
      }
      rethrow; // Re-throw to let calling code handle it
    }
  }

  // Get route by ID
  Future<RouteModel?> getRouteById(String routeId) async {
    try {
      final response =
          await SupabaseService.client
              .from('routes')
              .select('*')
              .eq('id', routeId)
              .single();

      return RouteModel.fromJson(response);
    } catch (e) {
      print('Error fetching route: $e');
      Fluttertoast.showToast(
        msg: "Error loading route details: ${e.toString()}",
        toastLength: Toast.LENGTH_LONG,
        gravity: ToastGravity.CENTER,
        backgroundColor: Colors.red,
        textColor: Colors.white,
        fontSize: 16.0,
      );
      return null;
    }
  }

  // Get stop by ID
  Future<BusStopModel?> getStopById(String stopId) async {
    try {
      final response =
          await SupabaseService.client
              .from('stops')
              .select('*')
              .eq('id', stopId)
              .single();

      return BusStopModel.fromJson(response);
    } catch (e) {
      print('Error fetching stop: $e');
      Fluttertoast.showToast(
        msg: "Error loading stop details: ${e.toString()}",
        toastLength: Toast.LENGTH_LONG,
        gravity: ToastGravity.CENTER,
        backgroundColor: Colors.red,
        textColor: Colors.white,
        fontSize: 16.0,
      );
      return null;
    }
  }

  // Simple distance calculation (Haversine formula)
  double _calculateDistance(
    double lat1,
    double lng1,
    double lat2,
    double lng2,
  ) {
    const double earthRadius = 6371; // km
    double dLat = _toRadians(lat2 - lat1);
    double dLng = _toRadians(lng2 - lng1);

    double a =
        math.sin(dLat / 2) * math.sin(dLat / 2) +
        math.cos(_toRadians(lat1)) *
            math.cos(_toRadians(lat2)) *
            math.sin(dLng / 2) *
            math.sin(dLng / 2);
    double c = 2.0 * math.atan2(math.sqrt(a), math.sqrt(1 - a));

    return earthRadius * c;
  }

  double _toRadians(double degrees) {
    return degrees * (math.pi / 180);
  }
}