import 'package:flutter/material.dart'; import 'package:intaleq_maps/intaleq_maps.dart'; import '../models/landmark.dart'; import '../services/landmark_database.dart'; import '../services/local_sqlite_db.dart'; import '../services/offline_routing_engine.dart'; import '../services/offline_routing_package_service.dart'; import '../services/tactical_api_service.dart'; class TacticalRoutePlannerSheet extends StatefulWidget { final LatLng currentGps; final LatLng? resectionFix; final LatLng? initialOrigin; final LatLng? initialDestination; final Function(LatLng origin, LatLng destination, TacticalVehicleProfile profile, bool autoStartNav) onRouteConfirmed; final VoidCallback onPickOriginOnMap; final VoidCallback onPickDestinationOnMap; final VoidCallback onClose; const TacticalRoutePlannerSheet({ super.key, required this.currentGps, this.resectionFix, this.initialOrigin, this.initialDestination, required this.onRouteConfirmed, required this.onPickOriginOnMap, required this.onPickDestinationOnMap, required this.onClose, }); @override State createState() => _TacticalRoutePlannerSheetState(); } class _TacticalRoutePlannerSheetState extends State { late LatLng _origin; String _originName = 'موقعي الحالي (GPS)'; LatLng? _destination; TacticalVehicleProfile _selectedProfile = TacticalVehicleProfile.convoy; // Search state final TextEditingController _destSearchCtrl = TextEditingController(); List _searchResults = []; bool _isSearching = false; @override void initState() { super.initState(); _origin = widget.initialOrigin ?? widget.resectionFix ?? widget.currentGps; if (widget.initialOrigin != null) { _originName = 'نقطة محددة من الخريطة (${widget.initialOrigin!.latitude.toStringAsFixed(4)}, ${widget.initialOrigin!.longitude.toStringAsFixed(4)})'; } else if (widget.resectionFix != null) { _originName = 'موقع الراصد المستخرج (التقاطع البصري)'; } if (widget.initialDestination != null) { _destination = widget.initialDestination; _destSearchCtrl.text = 'نقطة الهدف المحددة (${widget.initialDestination!.latitude.toStringAsFixed(4)}, ${widget.initialDestination!.longitude.toStringAsFixed(4)})'; } } @override void dispose() { _destSearchCtrl.dispose(); super.dispose(); } void _onSearch(String query) async { final cleanQuery = query.trim(); if (cleanQuery.isEmpty) { setState(() => _searchResults = []); return; } setState(() => _isSearching = true); List dbResults = []; try { dbResults = await LocalSqliteDb.searchPlaces(cleanQuery, limit: 25); } catch (_) {} final staticResults = LandmarkDatabase.search(cleanQuery); List onlineResults = []; if (dbResults.length < 5) { try { onlineResults = await TacticalApiService.searchOnlinePlaces(cleanQuery); } catch (_) {} } final combined = [...dbResults, ...onlineResults, ...staticResults]; final seen = {}; final unique = []; for (final r in combined) { if (seen.add('${r.name}_${r.lat}_${r.lng}')) { unique.add(r); } } if (mounted) { setState(() { _searchResults = unique; _isSearching = false; }); } } void _selectDestination(TacticalLandmark item) { setState(() { _destination = LatLng(item.lat, item.lng); _searchResults = []; _destSearchCtrl.text = '${item.name} (${item.region})'; }); } void _calculateAndPreview() { if (_destination == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( backgroundColor: Color(0xFFEF4444), content: Text('يرجى تحديد نقطة الوصول / الهدف أولاً.'), ), ); return; } widget.onClose(); widget.onRouteConfirmed(_origin, _destination!, _selectedProfile, false); } @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(16), decoration: const BoxDecoration( color: Color(0xFF090E1A), borderRadius: BorderRadius.vertical(top: Radius.circular(24)), border: Border(top: BorderSide(color: Color(0xFF0071E3), width: 2)), boxShadow: [BoxShadow(color: Colors.black87, blurRadius: 30)], ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( color: const Color(0xFF0071E3).withValues(alpha: 0.15), borderRadius: BorderRadius.circular(8), border: Border.all(color: const Color(0xFF0071E3)), ), child: const Icon(Icons.alt_route, color: Color(0xFF38BDF8), size: 20), ), const SizedBox(width: 10), const Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'مخطط مسارات القوافل التكتيكية (Routing)', style: TextStyle(color: Colors.white, fontSize: 13.5, fontWeight: FontWeight.bold), ), Text( 'حساب المسار وتضاريس الطرق وسرعة القوافل', style: TextStyle(color: Color(0xFF94A3B8), fontSize: 10.5), ), ], ), ], ), IconButton( onPressed: widget.onClose, icon: const Icon(Icons.close, color: Colors.white70, size: 20), ), ], ), const SizedBox(height: 12), // Real road network engine status (on-device Valhalla package) ValueListenableBuilder( valueListenable: OfflineRoutingPackageService.status, builder: (context, pkgStatus, _) { return FutureBuilder( future: OfflineRoutingPackageService.isInstalled(), builder: (context, snapshot) { final installed = snapshot.data ?? false; final color = installed ? const Color(0xFF22C55E) : const Color(0xFFF59E0B); final label = installed ? 'التوجيه المحلي: شبكة الطرق الحقيقية للأردن + SRTM جاهزة (بدون إنترنت)' : 'التوجيه المحلي الدقيق غير مثبت — نزّل حزمة الطرق من إعدادات الحزم'; return Container( margin: const EdgeInsets.only(bottom: 10), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: color.withValues(alpha: 0.08), borderRadius: BorderRadius.circular(8), border: Border.all(color: color.withValues(alpha: 0.4)), ), child: Row( children: [ Icon(installed ? Icons.route : Icons.download_rounded, color: color, size: 14), const SizedBox(width: 8), Expanded( child: Text( label, style: TextStyle(color: color, fontSize: 9.5), ), ), if (!installed && pkgStatus == RoutingPackageStatus.downloading) SizedBox( width: 10, height: 10, child: CircularProgressIndicator(strokeWidth: 1.5, color: color), ), ], ), ); }, ); }, ), // Origin & Destination Box Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xFF1E293B), borderRadius: BorderRadius.circular(14), border: Border.all(color: Colors.white12), ), child: Column( children: [ // Origin Row Row( children: [ const Icon(Icons.my_location, color: Color(0xFF22C55E), size: 18), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('نقطة الانطلاق (البداية):', style: TextStyle(color: Color(0xFF94A3B8), fontSize: 10)), Text( _originName, style: const TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis, ), ], ), ), IconButton( icon: const Icon(Icons.touch_app, size: 18, color: Color(0xFF38BDF8)), tooltip: 'تحديد نقطة البداية على الخريطة', onPressed: widget.onPickOriginOnMap, ), ], ), const Divider(color: Colors.white12, height: 16), // Destination Search Row Row( children: [ const Icon(Icons.location_on, color: Color(0xFFEF4444), size: 18), const SizedBox(width: 10), Expanded( child: TextField( controller: _destSearchCtrl, onChanged: _onSearch, style: const TextStyle(color: Colors.white, fontSize: 12.5), decoration: InputDecoration( hintText: 'ابحث عن نقطة الوصول أو الهدف...', hintStyle: const TextStyle(color: Color(0xFF64748B), fontSize: 11.5), isDense: true, border: InputBorder.none, contentPadding: const EdgeInsets.symmetric(vertical: 6), suffixIcon: _destSearchCtrl.text.isNotEmpty ? IconButton( icon: const Icon(Icons.clear, size: 14, color: Colors.white60), onPressed: () { _destSearchCtrl.clear(); _onSearch(''); }, ) : null, ), ), ), IconButton( icon: const Icon(Icons.touch_app, size: 18, color: Color(0xFFF59E0B)), tooltip: 'تحديد الهدف على الخريطة', onPressed: widget.onPickDestinationOnMap, ), ], ), ], ), ), if (_isSearching) ...[ const SizedBox(height: 4), const LinearProgressIndicator(minHeight: 2, color: Color(0xFF0071E3), backgroundColor: Colors.transparent), ], // Search Results Dropdown if (_searchResults.isNotEmpty) ...[ const SizedBox(height: 6), Container( constraints: const BoxConstraints(maxHeight: 160), decoration: BoxDecoration( color: const Color(0xFF1E293B), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.white12), ), child: ListView.separated( padding: EdgeInsets.zero, shrinkWrap: true, itemCount: _searchResults.length, separatorBuilder: (_, __) => const Divider(color: Colors.white10, height: 1), itemBuilder: (ctx, i) { final item = _searchResults[i]; return ListTile( dense: true, visualDensity: VisualDensity.compact, leading: Icon(item.typeIcon, size: 16, color: const Color(0xFF38BDF8)), title: Text(item.name, style: const TextStyle(color: Colors.white, fontSize: 11.5, fontWeight: FontWeight.bold)), subtitle: Text('${item.region} • ${item.typeLabelArabic}', style: const TextStyle(color: Color(0xFF64748B), fontSize: 10)), onTap: () => _selectDestination(item), ); }, ), ), ], const SizedBox(height: 12), // Vehicle Profiles Row( children: [ _buildProfileChip(TacticalVehicleProfile.convoy, 'قافلة عسكرية', Icons.local_shipping), const SizedBox(width: 6), _buildProfileChip(TacticalVehicleProfile.offroad4x4, 'دفع رباعي 4x4', Icons.directions_car), const SizedBox(width: 6), _buildProfileChip(TacticalVehicleProfile.rapidResponse, 'تدخل سريع', Icons.electric_bolt), ], ), const SizedBox(height: 14), // Action Button SizedBox( width: double.infinity, child: ElevatedButton.icon( onPressed: _calculateAndPreview, icon: const Icon(Icons.route, size: 18), label: const Text('حساب المسار وتضاريس الانحدار', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w900)), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF0071E3), foregroundColor: Colors.white, padding: const EdgeInsets.all(16), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), ), ), ), ], ), ); } Widget _buildProfileChip(TacticalVehicleProfile prof, String label, IconData icon) { final isSelected = _selectedProfile == prof; return Expanded( child: GestureDetector( onTap: () => setState(() => _selectedProfile = prof), child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: isSelected ? const Color(0xFF0071E3) : const Color(0xFF1E293B), borderRadius: BorderRadius.circular(10), border: Border.all(color: isSelected ? Colors.white : Colors.white12), ), child: Column( children: [ Icon(icon, size: 16, color: isSelected ? Colors.white : const Color(0xFF94A3B8)), const SizedBox(height: 3), Text( label, style: TextStyle( color: isSelected ? Colors.white : const Color(0xFF94A3B8), fontSize: 10, fontWeight: FontWeight.bold, ), ), ], ), ), ), ); } }