import "package:client/data.dart";
import "package:client/services.dart";
import "package:flutter/foundation.dart";
import "package:google_maps_flutter/google_maps_flutter.dart";

enum MarkerState {
  start,
  end;
}

mixin HomeMarkers on ChangeNotifier {
  MarkerState? markerState;
  bool get shouldShowMarkers => markerState != null;

  List<Stop>? stops;
  Marker? _startMarker;
  Marker? _endMarker;

  Set<Marker> get _filteredMarkers => {
    for (final stop in stops ?? <Stop>[])
      if (stop.routes.any(routesToShow.contains))
        Marker(
          markerId: MarkerId(stop.name),
          position: stop.coordinates.toLatLng(),
          onTap: () => onMarkerTapped(stop),
          consumeTapEvents: true,
          infoWindow: InfoWindow(
            title: stop.name,
            snippet: stop.summary,
          ),
        ),
  };

  Set<Marker> get markers => shouldShowMarkers ? _filteredMarkers : {
    if (_startMarker != null) _startMarker!,
    if (_endMarker != null) _endMarker!,
  };

  Set<String> bcRouteNames = {};
  Set<String> occtRouteNames = {};
  Set<String> routesToShow = {};
  Map<String, int> stopCounts = {};
  Iterable<(String, List<String>)> get providers => [
    ("OCCT", occtRouteNames.toList()..sort()),
    ("BC Transit", bcRouteNames.toList()..sort(compareNums)),
  ];

  int parseBcNumber(String routeName) {
    // eg, "53)" --> 53
    final first = routeName.split(" ").first;
    final withoutParen = first.substring(0, first.length - 1);
    return int.parse(withoutParen);
  }

  int compareNums(String a, String b) =>
    parseBcNumber(a).compareTo(parseBcNumber(b));

  MarkerId get markerId => switch (markerState!) {
    MarkerState.start => const MarkerId("start"),
    MarkerState.end => const MarkerId("end"),
  };

  BitmapDescriptor get markerIcon => switch (markerState!) {
    MarkerState.start => BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueCyan),
    MarkerState.end => BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen),
  };

  String get startOrEnd => switch (markerState!) {
    MarkerState.start => "Start",
    MarkerState.end => "End",
  };

  Future<void> updateMarkers() async {
    stops = await services.api.getStops();
    if (stops == null) return;
    for (final stop in stops!) {
      for (final route in stop.routes) {
        stopCounts[route] ??= 0;
        stopCounts[route] = stopCounts[route]! + 1;
      }
      final namesList = switch (stop.provider) {
        Provider.occt => occtRouteNames,
        Provider.bc => bcRouteNames,
      };
      namesList.addAll(stop.routes);
    }
    notifyListeners();
  }

  void showMarkers(MarkerState newState) {
    markerState = newState;
    notifyListeners();
  }

  void hideMarkers() {
    markerState = null;
    notifyListeners();
  }

  void onMarkerTapped(Stop stop) {
    final coordinates = stop.coordinates.toLatLng();
    final marker = Marker(
      markerId: markerId,
      position: coordinates,
      infoWindow: InfoWindow(
        title: "$startOrEnd at ${stop.name}",
        snippet: stop.description,
      ),
      icon: markerIcon,
    );
    switch (markerState!) {
      case MarkerState.start: updateStart(coordinates, marker);
      case MarkerState.end: updateEnd(coordinates, marker);
    }
  }

  @mustCallSuper
  void updateStart(LatLng coordinates, Marker marker) {
    _startMarker = marker;
    markerState = null;
    notifyListeners();
  }

  @mustCallSuper
  void updateEnd(LatLng coordinates, Marker marker) {
    _endMarker = marker;
    markerState = null;
    notifyListeners();
  }

  void showRoute(String route, {required bool shouldShow}) {
    if (shouldShow) {
      routesToShow.add(route);
    } else {
      routesToShow.remove(route);
    }
    notifyListeners();
  }
}
