import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; import 'package:intaleq_maps/intaleq_maps.dart'; import '../config/app_config.dart'; class TacticalRouteResult { final List points; final double distanceKm; final double durationMinutes; final String profile; TacticalRouteResult({ required this.points, required this.distanceKm, required this.durationMinutes, required this.profile, }); } class TacticalApiService { static String get defaultServerUrl => AppConfig.serverUrl; static String get defaultApiKey => AppConfig.apiKey; /// Calculate tactical route between waypoints using server routing engine static Future calculateTacticalRoute({ required LatLng origin, required LatLng destination, String profile = 'car', // 'car', 'offroad', 'convoy', 'emergency' String? serverUrl, String? apiKey, }) async { final url = serverUrl ?? defaultServerUrl; final key = apiKey ?? defaultApiKey; try { final uri = Uri.parse( '$url/api/maps/route?fromLat=${origin.latitude}&fromLng=${origin.longitude}' '&toLat=${destination.latitude}&toLng=${destination.longitude}' '&profile=$profile&steps=true&locale=ar', ); final resp = await http.get(uri, headers: {'x-api-key': key}).timeout(const Duration(seconds: 10)); if (resp.statusCode == 200) { final data = jsonDecode(resp.body); if (data['paths'] != null && (data['paths'] as List).isNotEmpty) { final path = data['paths'][0]; final distanceMeters = (path['distance'] as num?)?.toDouble() ?? 0.0; final timeMs = (path['time'] as num?)?.toDouble() ?? 0.0; final coords = []; if (path['points'] != null && path['points']['coordinates'] != null) { final List coordList = path['points']['coordinates']; for (final c in coordList) { coords.add(LatLng((c[1] as num).toDouble(), (c[0] as num).toDouble())); } } return TacticalRouteResult( points: coords, distanceKm: distanceMeters / 1000.0, durationMinutes: timeMs / (1000.0 * 60.0), profile: profile, ); } } } catch (e) { debugPrint('Tactical route API error: $e'); } return null; } /// Calculate Tactical Line of Sight (LOS) between Observer and Target static Future?> calculateLineOfSight({ required LatLng observer, required LatLng target, double observerHeight = 2.0, double targetHeight = 2.0, String? serverUrl, String? apiKey, }) async { final url = serverUrl ?? defaultServerUrl; final key = apiKey ?? defaultApiKey; try { final uri = Uri.parse( '$url/api/tactical/line-of-sight?observerLat=${observer.latitude}&observerLng=${observer.longitude}' '&targetLat=${target.latitude}&targetLng=${target.longitude}' '&observerHeight=$observerHeight&targetHeight=$targetHeight', ); final resp = await http.get(uri, headers: {'x-api-key': key}).timeout(const Duration(seconds: 10)); if (resp.statusCode == 200) { return jsonDecode(resp.body); } } catch (e) { debugPrint('Tactical LOS API error: $e'); } return null; } /// Calculate Tactical Isochrone Reachability Polygons static Future?> calculateIsochrone({ required LatLng center, List timeBuckets = const [300, 600, 900], // 5, 10, 15 minutes String? serverUrl, String? apiKey, }) async { final url = serverUrl ?? defaultServerUrl; final key = apiKey ?? defaultApiKey; try { final timesStr = timeBuckets.join(','); final uri = Uri.parse( '$url/api/tactical/isochrone?lat=${center.latitude}&lng=${center.longitude}×=$timesStr&profile=emergency', ); final resp = await http.get(uri, headers: {'x-api-key': key}).timeout(const Duration(seconds: 10)); if (resp.statusCode == 200) { return jsonDecode(resp.body); } } catch (e) { debugPrint('Tactical Isochrone API error: $e'); } return null; } }