import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'package:http/http.dart' as http;

import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'device_update_bloc.dart';
import '../../../product/constant/app/app_constants.dart';
import '../../../product/extention/context_extention.dart';
import '../../../product/services/language_services.dart';
import '../../../product/shared/find_location_maps/shared_map_search_location.dart';

import '../../../product/shared/find_location_maps/model/prediction_model.dart';
import '../../../product/shared/shared_transition.dart';
import 'geocode_model.dart';

showMapDialog(
    BuildContext context,
    DeviceUpdateBloc deviceUpdateBloc,
    TextEditingController latitudeController,
    TextEditingController longitudeController) async {
  const CameraPosition defaultPosition = CameraPosition(
    target: LatLng(20.985424, 105.738354),
    zoom: 12,
  );
  TextEditingController searchLocationController = TextEditingController();
  TextEditingController mapDialogLatitudeController = TextEditingController();
  TextEditingController mapDialogLongitudeController = TextEditingController();
  Completer<GoogleMapController> ggmapController = Completer();
  final streamController = StreamController<GoogleMapController>.broadcast();
  showGeneralDialog(
    barrierDismissible: false,
    transitionDuration: context.normalDuration,
    transitionBuilder: transitionsLeftToRight,
    context: context,
    pageBuilder: (context, animation, secondaryAnimation) {
      return StreamBuilder<Set<Marker>>(
          stream: deviceUpdateBloc.streamMarkers,
          builder: (context, markerSnapshot) {
            if (!markerSnapshot.hasData) {
              if (latitudeController.value.text != "" &&
                  longitudeController.value.text != "") {
                double latitude = double.parse(latitudeController.text);
                double longitude = double.parse(longitudeController.text);
                addMarker(
                  LatLng(latitude, longitude),
                  ggmapController,
                  deviceUpdateBloc,
                  mapDialogLatitudeController,
                  mapDialogLongitudeController,
                );
              }
            }
            return Scaffold(
              appBar: AppBar(
                title: Text(appLocalization(context)
                    .update_device_dialog_maps_dialog_title),
                centerTitle: true,
                actions: [
                  IconButton(
                      onPressed: () async {
                        String latitude = mapDialogLatitudeController.text;
                        String longitude = mapDialogLongitudeController.text;
                        log("Finish -- Latitude: $latitude, longitude: $longitude --");
                        getDataFromApi(latitude, longitude, deviceUpdateBloc);
                        latitudeController.text =
                            mapDialogLatitudeController.text;
                        longitudeController.text =
                            mapDialogLongitudeController.text;
                        bool isChange = true;
                        deviceUpdateBloc.sinkIsChanged.add(isChange);
                        Navigator.of(context).pop();
                      },
                      icon: const Icon(Icons.check))
                ],
              ),
              body: Stack(
                children: [
                  GoogleMap(
                    onTap: (location) async {
                      addMarker(
                        location,
                        ggmapController,
                        deviceUpdateBloc,
                        mapDialogLatitudeController,
                        mapDialogLongitudeController,
                      );
                    },
                    markers: markerSnapshot.data ?? {},
                    onMapCreated: (GoogleMapController mapController) {
                      ggmapController.complete(mapController);
                      streamController.add(mapController);
                    },
                    initialCameraPosition: defaultPosition,
                  ),
                  Container(
                    // color: Colors.white,
                    height: 80,
                    alignment: Alignment.topCenter,
                    padding: const EdgeInsets.all(10),
                    child: StreamBuilder<TextEditingController>(
                        stream: deviceUpdateBloc.streamSearchLocation,
                        builder: (context, searchLocation) {
                          return NearBySearchSFM(
                            textInputAction: TextInputAction.done,
                            textEditingController:
                                searchLocation.data ?? searchLocationController,
                            googleAPIKey: ApplicationConstants.MAP_KEY,
                            locationLatitude: 20.985424,
                            locationLongitude: 105.738354,
                            radius: 50000,
                            inputDecoration: InputDecoration(
                                hintText: appLocalization(context)
                                    .update_device_dialog_search_location_hint,
                                border: InputBorder.none),
                            debounceTime: 600,
                            isLatLngRequired: true,
                            getPlaceDetailWithLatLng: (Prediction prediction) {
                              FocusScope.of(context).unfocus();
                              addMarker(
                                  LatLng(double.parse(prediction.lat!),
                                      double.parse(prediction.lng!)),
                                  ggmapController,
                                  deviceUpdateBloc,
                                  mapDialogLatitudeController,
                                  mapDialogLongitudeController);
                            },
                            itemClick: (Prediction prediction) {
                              searchLocationController.text =
                                  prediction.structuredFormatting!.mainText!;
                              deviceUpdateBloc.sinkSearchLocation
                                  .add(searchLocationController);
                              searchLocationController.selection =
                                  TextSelection.fromPosition(TextPosition(
                                      offset: prediction.structuredFormatting!
                                          .mainText!.length));
                            },
                            boxDecoration: BoxDecoration(
                                color: Colors.white,
                                border: Border.all(
                                  color: Colors.grey,
                                  width: 0.5,
                                ),
                                borderRadius: const BorderRadius.all(
                                    Radius.circular(20))),
                          );
                        }),
                  )
                ],
              ),
            );
          });
    },
  );
}

addMarker(
  LatLng position,
  Completer<GoogleMapController> mapController,
  DeviceUpdateBloc deviceUpdateBloc,
  TextEditingController mapDialogLatitudeController,
  TextEditingController mapDialogLongitudeController,
) async {
  log("AddMarker -- Latitude: ${position.latitude}, longitude: ${position.longitude} --");

  Set<Marker> marker = {};
  deviceUpdateBloc.sinkMarkers.add(marker);
  Marker newMarker = Marker(
    markerId: const MarkerId('value'),
    position: LatLng(position.latitude, position.longitude),
    icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen),
    draggable: true,
    onDragEnd: (position) {
      mapDialogLatitudeController.text = position.latitude.toString();
      mapDialogLongitudeController.text = position.longitude.toString();
    },
  );
  marker.add(newMarker);
  deviceUpdateBloc.sinkMarkers.add(marker);
  mapDialogLatitudeController.text = position.latitude.toString();
  mapDialogLongitudeController.text = position.longitude.toString();
  updateCameraPosition(position, 14, mapController);
}

void getDataFromApi(String latitude, String longitude,
    DeviceUpdateBloc deviceUpdateBloc) async {
  String path =
      "maps/api/geocode/json?latlng=$latitude,$longitude&language=vi&result_type=political&key=${ApplicationConstants.MAP_KEY}";
  var url = Uri.parse('https://maps.googleapis.com/$path');

  final response = await http.get(url);

  if (response.statusCode != 200) {
    log("Loi: ${response.statusCode}");
    return;
  }

  Map<String, dynamic> data = jsonDecode(response.body);
  if (!data.containsKey('results') || data['results'].isEmpty) {
    log("Khong co result");
    return;
  }

  List<dynamic> results = data['results'];
  List<Geocode> geocodes =
      results.map((result) => Geocode.fromJson(result)).toList();

  Map<String, String> locations =
      _extractLocationComponents(geocodes[0].addressComponents!);

  // In ra thông tin của các location
  locations.forEach((key, value) {
    log("$key: $value");
  });

  await _processLocations(locations, deviceUpdateBloc);
}

Map<String, String> _extractLocationComponents(
    List<AddressComponent> addressComponents) {
  Map<String, String> locations = {};

  for (var addressComponent in addressComponents) {
    String longName = addressComponent.longName ?? "";
    if (addressComponent.types!.contains('administrative_area_level_3') ||
        addressComponent.types!.contains('sublocality_level_1')) {
      locations['wardkey'] = longName;
    } else if (addressComponent.types!
            .contains('administrative_area_level_2') ||
        addressComponent.types!.contains('sublocality_level_2') ||
        addressComponent.types!.contains('locality')) {
      locations['districtkey'] = longName;
    } else if (addressComponent.types!
        .contains('administrative_area_level_1')) {
      locations['provincekey'] = longName;
    }
  }

  return locations;
}

Future<void> _processLocations(
    Map<String, String> locations, DeviceUpdateBloc deviceUpdateBloc) async {
  String provinceNameFromAPI = locations['provincekey'] ?? "";
  String districtNameFromAPI = locations['districtkey'] ?? "";
  String wardNameFromAPI = locations['wardkey'] ?? "";

  final province =
      await deviceUpdateBloc.getProvinceByName(provinceNameFromAPI);
  if (province.name != "null") {
    log("Province: ${province.fullName}, ProvinceCode: ${province.code}");
    deviceUpdateBloc.sinkProvinceData
        .add({"code": province.code!, "name": province.fullName!});
    deviceUpdateBloc.getAllProvinces();

    final district = await deviceUpdateBloc.getDistrictByName(
        districtNameFromAPI, province.code!);
    log("Districtname: ${district.fullName}, districtCode: ${district.code}");
    deviceUpdateBloc.getAllDistricts(province.code!);
    if (district.name != "null") {
      deviceUpdateBloc.sinkDistrictData
          .add({"code": district.code!, "name": district.fullName!});
      final ward =
          await deviceUpdateBloc.getWardByName(wardNameFromAPI, district.code!);
      log("Wardname: ${ward.fullName}, WardCode: ${ward.code}");
      deviceUpdateBloc.getAllWards(district.code!);
      if (ward.name != "null") {
        log("Xac dinh duoc het thong tin tu toa do");
        deviceUpdateBloc.sinkWardData
            .add({"code": ward.code!, "name": ward.fullName!});
      } else {
        deviceUpdateBloc.sinkWardData.add({});
      }
    } else {
      deviceUpdateBloc.sinkDistrictData.add({});
    }
  } else {
    deviceUpdateBloc.sinkProvinceData.add({});
  }
}

Future<void> updateCameraPosition(LatLng location, double zoom,
    Completer<GoogleMapController> mapController) async {
  final CameraPosition cameraPosition = CameraPosition(
    target: LatLng(
      location.latitude,
      location.longitude,
    ),
    zoom: zoom,
  );
  final GoogleMapController mapControllerNew = await mapController.future;
  mapControllerNew.animateCamera(
    CameraUpdate.newCameraPosition(
      cameraPosition,
    ),
  );
}
