import React, {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react";
import { View, StyleSheet } from "react-native";
import Maplibre from "@maplibre/maplibre-react-native";
import polyline from "@mapbox/polyline";
import { MaterialCommunityIcons } from "@expo/vector-icons";
import Drawer from "react-native-drawer";

import MapView from "~/containers/Map/MapView";
import Camera from "~/containers/Map/Camera";
import LastKnownLocationMarker from "~/containers/Map/LastKnownLocationMarker";
import { DEFAULT_ZOOM_LEVEL } from "~/containers/Map/constants";
import StepZoomButtonGroup from "~/containers/Map/StepZoomButtonGroup";

import Text from "~/components/Text";
import IconTouchTarget from "~/components/IconTouchTarget";
import { useTheme } from "~/theme";
import { useDefibsState, useNetworkState } from "~/stores";
import useLocation from "~/hooks/useLocation";
import { getDefibAvailability } from "~/utils/dae/getDefibAvailability";
import { osmProfileUrl, profileDefaultModes } from "~/scenes/AlertCurMap/routing";
import { routeToInstructions } from "~/lib/geo/osrmTextInstructions";
import {
  announceForA11yIfScreenReaderEnabled,
  setA11yFocusAfterInteractions,
} from "~/lib/a11y";

import RoutingSteps from "~/scenes/AlertCurMap/RoutingSteps";
import MapHeadRouting from "~/scenes/AlertCurMap/MapHeadRouting";

import {
  STATE_CALCULATING_INIT,
  STATE_CALCULATING_LOADED,
  STATE_CALCULATING_LOADING,
} from "~/scenes/AlertCurMap/constants";

const STATUS_COLORS = {
  open: "#4CAF50",
  closed: "#F44336",
  unknown: "#9E9E9E",
};

export default React.memo(function DAEItemCarte() {
  const { colors } = useTheme();
  const { selectedDefib: defib } = useDefibsState(["selectedDefib"]);
  const { hasInternetConnection } = useNetworkState(["hasInternetConnection"]);
  const { coords, isLastKnown, lastKnownTimestamp } = useLocation();

  const mapRef = useRef();
  const cameraRef = useRef();
  const [cameraKey, setCameraKey] = useState(1);
  const [zoomLevel, setZoomLevel] = useState(DEFAULT_ZOOM_LEVEL);
  const abortControllerRef = useRef(null);

  const refreshCamera = useCallback(() => {
    setCameraKey(`${Date.now()}`);
  }, []);

  const hasUserCoords =
    coords && coords.latitude !== null && coords.longitude !== null;
  const hasDefibCoords = defib && defib.latitude && defib.longitude;

  const [routeCoords, setRouteCoords] = useState(null);
  const [routeError, setRouteError] = useState(null);
  const [loadingRoute, setLoadingRoute] = useState(false);
  const [route, setRoute] = useState(null);
  const [calculating, setCalculating] = useState(STATE_CALCULATING_INIT);

  const defaultProfile = "foot";
  const [profile, setProfile] = useState(defaultProfile);

  // Compute route
  useEffect(() => {
    if (!hasUserCoords || !hasDefibCoords || !hasInternetConnection) {
      return;
    }

    // Abort any previous request
    if (abortControllerRef.current) {
      abortControllerRef.current.abort();
    }

    const controller = new AbortController();
    abortControllerRef.current = controller;

    const fetchRoute = async () => {
      setLoadingRoute(true);
      setCalculating(STATE_CALCULATING_LOADING);
      setRouteError(null);
      try {
        const origin = `${coords.longitude},${coords.latitude}`;
        const target = `${defib.longitude},${defib.latitude}`;
        const osrmUrl = osmProfileUrl[profile] || osmProfileUrl.foot;
        const url = `${osrmUrl}/route/v1/${profile}/${origin};${target}?overview=full&steps=true`;

        const res = await fetch(url, { signal: controller.signal });
        const result = await res.json();

        if (result.routes && result.routes.length > 0) {
          const fetchedRoute = result.routes[0];
          const decoded = polyline
            .decode(fetchedRoute.geometry)
            .map((p) => p.reverse());
          setRouteCoords(decoded);
          setRoute(fetchedRoute);
          setCalculating(STATE_CALCULATING_LOADED);
        }
      } catch (err) {
        if (err.name !== "AbortError") {
          console.warn("Route calculation failed:", err.message);
          setRouteError(err);
        }
      } finally {
        setLoadingRoute(false);
      }
    };

    fetchRoute();

    return () => {
      controller.abort();
    };
  }, [
    hasUserCoords,
    hasDefibCoords,
    hasInternetConnection,
    coords,
    defib,
    profile,
  ]);

  // Compute instructions from route steps
  const allSteps = useMemo(() => {
    if (!route) return [];
    return route.legs.flatMap((leg) => leg.steps);
  }, [route]);

  const instructions = useMemo(() => {
    if (allSteps.length === 0) return [];
    return routeToInstructions(allSteps);
  }, [allSteps]);

  const distance = useMemo(
    () => allSteps.reduce((acc, step) => acc + (step?.distance || 0), 0),
    [allSteps],
  );
  const duration = useMemo(
    () => allSteps.reduce((acc, step) => acc + (step?.duration || 0), 0),
    [allSteps],
  );

  const destinationName = useMemo(() => {
    if (!route) return defib?.nom || "";
    const { legs } = route;
    const lastLeg = legs[legs.length - 1];
    if (!lastLeg) return defib?.nom || "";
    const { steps } = lastLeg;
    const lastStep = steps[steps.length - 1];
    return lastStep?.name || defib?.nom || "";
  }, [route, defib]);

  // Stepper drawer state
  const [stepperIsOpened, setStepperIsOpened] = useState(false);
  const routingSheetTitleA11yRef = useRef(null);
  const a11yStepsEntryRef = useRef(null);
  const mapHeadOpenRef = useRef(null);
  const mapHeadSeeAllRef = useRef(null);
  const lastStepsTriggerRef = useRef(null);

  const openStepper = useCallback(
    (triggerRef) => {
      if (triggerRef) {
        lastStepsTriggerRef.current = triggerRef;
      }
      setStepperIsOpened(true);
    },
    [],
  );

  const closeStepper = useCallback(() => {
    setStepperIsOpened(false);
    setA11yFocusAfterInteractions(lastStepsTriggerRef);
  }, []);

  const stepperOnOpen = useCallback(() => {
    if (!stepperIsOpened) {
      setStepperIsOpened(true);
    }
    setA11yFocusAfterInteractions(routingSheetTitleA11yRef);
    announceForA11yIfScreenReaderEnabled("Liste des étapes ouverte");
  }, [stepperIsOpened]);

  const stepperOnClose = useCallback(() => {
    if (stepperIsOpened) {
      setStepperIsOpened(false);
    }
    announceForA11yIfScreenReaderEnabled("Liste des étapes fermée");
    setA11yFocusAfterInteractions(lastStepsTriggerRef);
  }, [stepperIsOpened]);

  // Defib marker GeoJSON
  const defibGeoJSON = useMemo(() => {
    if (!hasDefibCoords) return null;
    const { status } = getDefibAvailability(
      defib.horaires_std,
      defib.disponible_24h,
    );
    return {
      type: "FeatureCollection",
      features: [
        {
          type: "Feature",
          geometry: {
            type: "Point",
            coordinates: [defib.longitude, defib.latitude],
          },
          properties: {
            id: defib.id,
            nom: defib.nom || "Défibrillateur",
            color: STATUS_COLORS[status],
          },
        },
      ],
    };
  }, [defib, hasDefibCoords]);

  // Route line GeoJSON
  const routeGeoJSON = useMemo(() => {
    if (!routeCoords || routeCoords.length < 2) return null;
    return {
      type: "Feature",
      geometry: {
        type: "LineString",
        coordinates: routeCoords,
      },
    };
  }, [routeCoords]);

  // Camera bounds to show both user + defib
  const bounds = useMemo(() => {
    if (!hasUserCoords || !hasDefibCoords) return null;
    const lats = [coords.latitude, defib.latitude];
    const lons = [coords.longitude, defib.longitude];
    return {
      ne: [Math.max(...lons), Math.max(...lats)],
      sw: [Math.min(...lons), Math.min(...lats)],
    };
  }, [hasUserCoords, hasDefibCoords, coords, defib]);

  const profileDefaultMode = profileDefaultModes[profile];

  if (!defib) return null;

  return (
    <View style={styles.container}>
      {/* Offline banner */}
      {!hasInternetConnection && (
        <View
          style={[
            styles.offlineBanner,
            { backgroundColor: (colors.error || "#F44336") + "15" },
          ]}
        >
          <MaterialCommunityIcons
            name="wifi-off"
            size={18}
            color={colors.error || "#F44336"}
          />
          <Text
            style={[
              styles.offlineBannerText,
              { color: colors.error || "#F44336" },
            ]}
          >
            Hors ligne — l'itinéraire n'est pas disponible
          </Text>
        </View>
      )}

      <Drawer
        type="overlay"
        tweenHandler={(ratio) => ({
          main: { opacity: (2 - ratio) / 2 },
        })}
        tweenDuration={250}
        openDrawerOffset={40}
        open={stepperIsOpened}
        onOpen={stepperOnOpen}
        onClose={stepperOnClose}
        tapToClose
        negotiatePan
        content={
          <RoutingSteps
            setProfile={setProfile}
            profile={profile}
            closeStepper={closeStepper}
            destinationName={destinationName}
            distance={distance}
            duration={duration}
            instructions={instructions}
            calculatingState={calculating}
            titleA11yRef={routingSheetTitleA11yRef}
          />
        }
      >
        <View style={{ flex: 1 }}>
          {/* A11y entry point for routing steps */}
          <IconTouchTarget
            ref={a11yStepsEntryRef}
            accessibilityLabel="Ouvrir la liste des étapes de l'itinéraire"
            accessibilityHint="Affiche la destination, la distance, la durée et toutes les étapes sans utiliser la carte."
            onPress={() => openStepper(a11yStepsEntryRef)}
            style={({ pressed }) => ({
              position: "absolute",
              top: 4,
              left: 4,
              zIndex: 10,
              backgroundColor: colors.surface,
              borderRadius: 8,
              opacity: pressed ? 0.7 : 1,
            })}
          >
            <MaterialCommunityIcons
              name="format-list-bulleted"
              size={24}
              color={colors.onSurface}
            />
          </IconTouchTarget>

          <MapView
            mapRef={mapRef}
            compassViewPosition={1}
            compassViewMargin={{ x: 10, y: 10 }}
          >
            <Camera
              cameraKey={cameraKey}
              setCameraKey={setCameraKey}
              refreshCamera={refreshCamera}
              cameraRef={cameraRef}
              followUserLocation={!bounds}
              followUserMode={
                bounds
                  ? Maplibre.UserTrackingMode.None
                  : Maplibre.UserTrackingMode.Follow
              }
              followPitch={0}
              zoomLevel={zoomLevel}
              bounds={bounds}
              detached={false}
            />

            {/* Route line */}
            {routeGeoJSON && (
              <Maplibre.ShapeSource id="routeSource" shape={routeGeoJSON}>
                <Maplibre.LineLayer
                  id="routeLineLayer"
                  style={{
                    lineColor: "rgba(49, 76, 205, 0.84)",
                    lineWidth: 4,
                    lineCap: "round",
                    lineJoin: "round",
                    lineOpacity: 0.84,
                  }}
                />
              </Maplibre.ShapeSource>
            )}

            {/* Defib marker */}
            {defibGeoJSON && (
              <Maplibre.ShapeSource id="defibItemSource" shape={defibGeoJSON}>
                <Maplibre.CircleLayer
                  id="defibItemCircle"
                  style={{
                    circleRadius: 10,
                    circleColor: ["get", "color"],
                    circleStrokeColor: "#FFFFFF",
                    circleStrokeWidth: 2.5,
                  }}
                />
                <Maplibre.SymbolLayer
                  id="defibItemLabel"
                  aboveLayerID="defibItemCircle"
                  style={{
                    textField: ["get", "nom"],
                    textSize: 12,
                    textOffset: [0, 1.8],
                    textAnchor: "top",
                    textMaxWidth: 14,
                    textColor: colors.onSurface,
                    textHaloColor: colors.surface,
                    textHaloWidth: 1,
                  }}
                />
              </Maplibre.ShapeSource>
            )}

            {/* User location */}
            {isLastKnown && hasUserCoords ? (
              <LastKnownLocationMarker
                coordinates={coords}
                timestamp={lastKnownTimestamp}
                id="lastKnownLocation_daeItem"
              />
            ) : (
              <Maplibre.UserLocation visible showsUserHeadingIndicator />
            )}
          </MapView>

          {/* Head routing step overlay */}
          {instructions.length > 0 && (
            <MapHeadRouting
              instructions={instructions}
              distance={distance}
              profileDefaultMode={profileDefaultMode}
              openStepper={openStepper}
              openStepperTriggerRef={mapHeadOpenRef}
              seeAllStepsTriggerRef={mapHeadSeeAllRef}
              calculatingState={calculating}
            />
          )}
        </View>
      </Drawer>

      <StepZoomButtonGroup mapRef={mapRef} setZoomLevel={setZoomLevel} />

      {/* Route error */}
      {routeError && !loadingRoute && (
        <View style={styles.routeErrorOverlay}>
          <Text
            style={[
              styles.routeErrorText,
              { color: colors.onSurfaceVariant || colors.grey },
            ]}
          >
            Impossible de calculer l'itinéraire
          </Text>
        </View>
      )}
    </View>
  );
});

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  offlineBanner: {
    flexDirection: "row",
    alignItems: "center",
    paddingHorizontal: 16,
    paddingVertical: 10,
    gap: 8,
  },
  offlineBannerText: {
    fontSize: 13,
    flex: 1,
  },
  routeErrorOverlay: {
    position: "absolute",
    bottom: 16,
    left: 16,
    right: 16,
    alignItems: "center",
  },
  routeErrorText: {
    fontSize: 13,
    textAlign: "center",
  },
});
