import 'dart:async'; // ignore: avoid_web_libraries_in_flutter import 'dart:js' as js; import 'package:flutter/foundation.dart'; import 'package:prosapp_web_app/services/api_service.dart'; // Result of a reverse geocode via the Maps JS SDK Geocoder class GeocodeResult { final String formattedAddress; final List> addressComponents; const GeocodeResult({required this.formattedAddress, required this.addressComponents}); } class MapsService { static bool _loaded = false; static Completer? _completer; static Future load() async { if (_loaded) return true; if (_completer != null) return _completer!.future; _completer = Completer(); try { final data = await ApiService.instance.get('/settings/maps-key'); final apiKey = data['api_key'] as String? ?? ''; if (apiKey.isEmpty) { debugPrint('MapsService: API key no configurada'); _completer!.complete(false); return false; } if (kIsWeb) { _injectScript(apiKey); } _loaded = true; _completer!.complete(true); return true; } catch (e) { debugPrint('MapsService: error cargando key — $e'); _completer!.complete(false); return false; } } // Uses the already-loaded Maps JS SDK Geocoder — no separate Geocoding API enablement needed. static Future reverseGeocode(double lat, double lng) async { final completer = Completer(); final id = '_gc${DateTime.now().millisecondsSinceEpoch}'; try { js.context['${id}_ok'] = js.allowInterop((String address, String compsJson) { try { js.context.deleteProperty('${id}_ok'); } catch (_) {} try { final rawList = js.context.callMethod('eval', ['JSON.parse(\'${compsJson.replaceAll("'", "\\'")}\')']); final comps = >[]; // rawList is a JS array; iterate by index final len = (rawList['length'] as num?)?.toInt() ?? 0; for (var i = 0; i < len; i++) { final item = rawList[i] as js.JsObject; final types = []; final tLen = (item['types']['length'] as num?)?.toInt() ?? 0; for (var t = 0; t < tLen; t++) types.add(item['types'][t].toString()); comps.add({'long_name': item['long_name'].toString(), 'types': types}); } completer.complete(GeocodeResult(formattedAddress: address, addressComponents: comps)); } catch (_) { completer.complete(GeocodeResult(formattedAddress: address, addressComponents: [])); } }); js.context['${id}_err'] = js.allowInterop(() { try { js.context.deleteProperty('${id}_err'); } catch (_) {} completer.complete(null); }); js.context.callMethod('eval', [''' (function(){ try { var g = new google.maps.Geocoder(); g.geocode({location:{lat:$lat,lng:$lng},language:'es'}, function(r,s){ if(s==='OK'&&r&&r.length>0){ var compsStr = JSON.stringify(r[0].address_components.map(function(c){ return {long_name:c.long_name,types:c.types}; })); window['${id}_ok'](r[0].formatted_address, compsStr); } else { window['${id}_err'](); } }); } catch(e){ window['${id}_err'](); } })(); ''']); } catch (e) { completer.complete(null); } return completer.future.timeout(const Duration(seconds: 10), onTimeout: () => null); } static void _injectScript(String apiKey) { // Verifica si ya está cargado final existing = js.context.callMethod('eval', [ 'typeof google !== "undefined" && typeof google.maps !== "undefined"' ]); if (existing == true) { _loaded = true; return; } js.context.callMethod('eval', [''' (function() { var s = document.createElement('script'); s.src = 'https://maps.googleapis.com/maps/api/js?key=$apiKey'; s.async = true; document.head.appendChild(s); })(); ''']); } }