import 'dart:async';
import 'dart:convert';
import 'dart:developer';

import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_cluster_manager_2/google_maps_cluster_manager_2.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart'
    hide ClusterManager, Cluster;
import 'package:sfm_app/feature/devices/device_model.dart';
import 'package:sfm_app/bloc/map_bloc.dart';
import 'package:sfm_app/feature/map/widget/on_tap_marker_widget.dart';
import 'package:sfm_app/product/base/bloc/base_bloc.dart';
import 'package:sfm_app/product/constant/icon/icon_constants.dart';
import 'package:sfm_app/product/permission/location_permission.dart';
import 'package:sfm_app/product/services/api_services.dart';
import '../../product/constant/enums/app_theme_enums.dart';

class MapScreen extends StatefulWidget {
  const MapScreen({super.key});

  @override
  State<MapScreen> createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
  late BitmapDescriptor normalIcon;
  late BitmapDescriptor offlineIcon;
  late BitmapDescriptor abnormalIcon;
  late BitmapDescriptor flameIcon;
  late BitmapDescriptor hospitalIcon;
  late BitmapDescriptor fireStationIcon;
  late MapBloc mapBloc;
  late ClusterManager clusterManager;
  // MapType mapType = MapType.terrain;
  APIServices apiServices = APIServices();
  final streamController = StreamController<GoogleMapController>.broadcast();
  List<Device> devices = [];
  Completer<GoogleMapController> _controller = Completer();
  List<String> imageAssets = [
    IconConstants.instance.getIcon("normal_icon"),
    IconConstants.instance.getIcon("offline_icon"),
    IconConstants.instance.getIcon("flame_icon"),
    IconConstants.instance.getIcon("hospital_marker"),
    IconConstants.instance.getIcon("fire_station_marker"),
  ];
  static const CameraPosition _myPosition = CameraPosition(
    target: LatLng(20.976108, 105.791666),
    zoom: 12,
  );
  Set<Marker> markersAll = {};
  List<Marker> markers = [];
  // LatLng myLocation = const LatLng(213761, 123123);
  // Position? position;
  // bool isAllowLocationPermission = false;
  String themeMode = '';
  @override
  void initState() {
    super.initState();
    mapBloc = BlocProvider.of(context);
    _loadIcons();
    getAllMarkers();
    clusterManager = _initClusterManager();
  }

  @override
  void dispose() {
    super.dispose();
    _controller = Completer();
    streamController.close();
  }

  void onMapCreated(GoogleMapController controller) {
    _controller.complete(controller);
    streamController.add(controller);
    clusterManager.setMapId(controller.mapId);
    checkTheme();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          StreamBuilder<Set<Marker>>(
            stream: mapBloc.streamAllMarker,
            builder: (context, markerSnapshot) {
              return StreamBuilder<List<LatLng>>(
                stream: mapBloc.streamPolylines,
                builder: (context, polylinesSnapshot) {
                  return StreamBuilder<String>(
                    stream: mapBloc.streamMapTheme,
                    builder: (context, mapThemeSnapshot) {
                      return GoogleMap(
                        initialCameraPosition: _myPosition,
                        // mapType: mapType,
                        onMapCreated: onMapCreated,
                        markers: markerSnapshot.data ?? markersAll
                          ..addAll(markers),
                        zoomControlsEnabled: false,
                        myLocationEnabled: true,
                        mapToolbarEnabled: false,
                        onCameraMove: (position) {
                          clusterManager.onCameraMove(position);
                        },
                        onCameraIdle: () {
                          clusterManager.updateMap();
                        },
                        polylines: {
                          Polyline(
                            polylineId: const PolylineId('router'),
                            points: polylinesSnapshot.data ?? [],
                            color: Colors.deepPurpleAccent,
                            width: 8,
                          ),
                        },
                        style: mapThemeSnapshot.data,
                      );
                    },
                  );
                },
              );
            },
          ),
        ],
      ),
    );
  }

  void checkTheme() async {
    String theme = await apiServices.checkTheme();
    if (theme == AppThemes.LIGHT.name) {
      themeMode = '';
    } else {
      themeMode = await _getFileData('assets/map_themes/maps_dark_theme.json');
    }
    mapBloc.sinkMapTheme.add(themeMode);
  }

  Future<String> _getFileData(String path) async {
    return rootBundle.loadString(path);
  }


  Future<void> _loadIcons() async {
    List<Future<BitmapDescriptor>> iconFutures = imageAssets.map((asset) {
      return BitmapDescriptor.asset(
          const ImageConfiguration(size: Size(50, 50)), asset);
    }).toList();

    List<BitmapDescriptor> icons = await Future.wait(iconFutures);

    normalIcon = icons[0];
    offlineIcon = icons[1];
    flameIcon = icons[2];
    hospitalIcon = icons[3];
    fireStationIcon = icons[4];
  }

  ClusterManager _initClusterManager() {
    return ClusterManager<Device>(
      devices,
      _updateMarkers,
      markerBuilder: _getmarkerBuilder(),
    );
  }

  Future<Marker> Function(Cluster<Device>) _getmarkerBuilder() =>
      (cluster) async {
        return Marker(
          markerId: MarkerId(
            cluster.getId(),
          ),
          position: cluster.location,
          onTap: () async {
            bool check = await checkLocationPermission(context);
            if (check == true) {
              Position position = await Geolocator.getCurrentPosition();
              onTapMarker(
                // ignore: use_build_context_synchronously
                context,
                _controller,
                mapBloc,
                LatLng(position.latitude, position.longitude),
                cluster.items,
                imageAssets,
                markers,
                hospitalIcon,
                fireStationIcon,
              );
            } else {
              // ignore: use_build_context_synchronously
              ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(content: Text("Cannot get your Location")));
            }
          },
          icon: getMarkerIcon(cluster),
        );
      };

  BitmapDescriptor getMarkerIcon(Cluster<Device> cluster) {
    if (cluster.items.length == 1) {
      Device item = cluster.items.first;
      if (item.state == 0) {
        return normalIcon;
      } else if (item.state == 1) {
        return flameIcon;
      } else {
        return offlineIcon;
      }
    }
    bool hasStateOne = false;
    bool hasOtherState = false;

    for (var item in cluster.items) {
      if (item.state == 1) {
        hasStateOne = true;
        break;
      } else if (item.state != 0) {
        hasOtherState = true;
      }
    }

    // log("Has state = 1: $hasStateOne, Has other state: $hasOtherState");

    if (hasStateOne) {
      return flameIcon; // flameIcon
    } else if (hasOtherState) {
      return normalIcon; // normalIcon
    } else {
      return offlineIcon; // offlineIcon
    }
  }

  bool checkStateMarker(Cluster<Device> cluster) {
    bool hasStateOne = false;
    bool hasOtherState = false;

    for (var item in cluster.items) {
      if (item.state == 1) {
        hasStateOne = true;
        break;
      } else if (item.state != 0) {
        hasOtherState = true;
      }
    }
    
    if (hasStateOne) {
      return true; // flameIcon
    } else if (hasOtherState) {
      return false; // normalIcon
    } else {
      return true; // offlineIcon
    }
  }

  void _updateMarkers(Set<Marker> marker) {
    log("Update Marker");
    checkTheme();
    markersAll = marker;
    mapBloc.sinkAllMarker.add(marker);
  }

  void getAllMarkers() async {
    String response = await apiServices.getOwnerDevices();
    if (response != "") {
      final data = jsonDecode(response);
      List<dynamic> result = data['items'];
      final devicesList = Device.fromJsonDynamicList(result);
      for (var device in devicesList) {
        devices.add(device);
      }
    }
  }

  Future<bool> checkLocationPermission(context) async {
    bool check = await LocationPermissionRequest.instance
        .checkLocationPermission(context);
    return check;
  }
}
