import Fuse from "fuse.js";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { usePageTitle } from "~/contexts/PageTitleContext";
import { useApp } from "../AppContext";
import StopGallery from "../components/StopGallery";
import StopItem from "../components/StopItem";
import StopItemSkeleton from "../components/StopItemSkeleton";
import StopDataProvider, { type Stop } from "../data/StopDataProvider";
import "./home.css";

export default function StopList() {
  const { t } = useTranslation();
  usePageTitle(t("navbar.stops", "Paradas"));
  const { region } = useApp();
  const [data, setData] = useState<Stop[] | null>(null);
  const [loading, setLoading] = useState(true);
  const [searchResults, setSearchResults] = useState<Stop[] | null>(null);
  const [favouriteIds, setFavouriteIds] = useState<number[]>([]);
  const [recentIds, setRecentIds] = useState<number[]>([]);
  const [favouriteStops, setFavouriteStops] = useState<Stop[]>([]);
  const [recentStops, setRecentStops] = useState<Stop[]>([]);
  const [userLocation, setUserLocation] = useState<{
    latitude: number;
    longitude: number;
  } | null>(null);
  const searchTimeout = useRef<NodeJS.Timeout | null>(null);

  const randomPlaceholder = useMemo(
    () => t("stoplist.search_placeholder"),
    [t]
  );

  const fuse = useMemo(
    () =>
      new Fuse(data || [], {
        threshold: 0.3,
        keys: ["name.original", "name.intersect", "stopId"],
      }),
    [data]
  );

  const requestUserLocation = useCallback(() => {
    if (typeof window === "undefined" || !("geolocation" in navigator)) {
      return;
    }

    navigator.geolocation.getCurrentPosition(
      (position) => {
        setUserLocation({
          latitude: position.coords.latitude,
          longitude: position.coords.longitude,
        });
      },
      (error) => {
        console.warn("Unable to obtain user location", error);
      },
      {
        enableHighAccuracy: false,
        maximumAge: 5 * 60 * 1000,
      }
    );
  }, []);

  useEffect(() => {
    if (typeof window === "undefined" || !("geolocation" in navigator)) {
      return;
    }

    let permissionStatus: PermissionStatus | null = null;

    const handlePermissionChange = () => {
      if (permissionStatus?.state === "granted") {
        requestUserLocation();
      }
    };

    const checkPermission = async () => {
      try {
        if (navigator.permissions?.query) {
          permissionStatus = await navigator.permissions.query({
            name: "geolocation",
          });
          if (permissionStatus.state === "granted") {
            requestUserLocation();
          }
          permissionStatus.addEventListener("change", handlePermissionChange);
        } else {
          requestUserLocation();
        }
      } catch (error) {
        console.warn("Geolocation permission check failed", error);
        requestUserLocation();
      }
    };

    checkPermission();

    return () => {
      permissionStatus?.removeEventListener("change", handlePermissionChange);
    };
  }, [requestUserLocation]);

  // Sort stops by proximity when we know where the user is located.
  const sortedAllStops = useMemo(() => {
    if (!data) {
      return [] as Stop[];
    }

    if (!userLocation) {
      return [...data].sort((a, b) => a.stopId - b.stopId);
    }

    const toRadians = (value: number) => (value * Math.PI) / 180;
    const getDistance = (
      lat1: number,
      lon1: number,
      lat2: number,
      lon2: number
    ) => {
      const R = 6371000; // meters
      const dLat = toRadians(lat2 - lat1);
      const dLon = toRadians(lon2 - lon1);
      const a =
        Math.sin(dLat / 2) * Math.sin(dLat / 2) +
        Math.cos(toRadians(lat1)) *
          Math.cos(toRadians(lat2)) *
          Math.sin(dLon / 2) *
          Math.sin(dLon / 2);
      const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
      return R * c;
    };

    return data
      .map((stop) => {
        if (
          typeof stop.latitude !== "number" ||
          typeof stop.longitude !== "number"
        ) {
          return { stop, distance: Number.POSITIVE_INFINITY };
        }

        const distance = getDistance(
          userLocation.latitude,
          userLocation.longitude,
          stop.latitude,
          stop.longitude
        );

        return { stop, distance };
      })
      .sort((a, b) => {
        if (a.distance === b.distance) {
          return a.stop.stopId - b.stop.stopId;
        }
        return a.distance - b.distance;
      })
      .map(({ stop }) => stop);
  }, [data, userLocation]);

  // Load favourite and recent IDs immediately from localStorage
  useEffect(() => {
    setFavouriteIds(StopDataProvider.getFavouriteIds(region));
    setRecentIds(StopDataProvider.getRecent(region));
  }, [region]);

  // Load stops from network
  const loadStops = useCallback(async () => {
    try {
      setLoading(true);

      const stops = await StopDataProvider.loadStopsFromNetwork(region);

      // Add favourite flags to stops
      const favouriteStopsIds = StopDataProvider.getFavouriteIds(region);
      const stopsWithFavourites = stops.map((stop) => ({
        ...stop,
        favourite: favouriteStopsIds.includes(stop.stopId),
      }));

      setData(stopsWithFavourites);

      // Update favourite and recent stops with full data
      const favStops = stopsWithFavourites.filter((stop) =>
        favouriteStopsIds.includes(stop.stopId)
      );
      setFavouriteStops(favStops);

      const recIds = StopDataProvider.getRecent(region);
      const recStops = recIds
        .map((id) => stopsWithFavourites.find((stop) => stop.stopId === id))
        .filter(Boolean) as Stop[];
      setRecentStops(recStops.reverse());
    } catch (error) {
      console.error("Failed to load stops:", error);
    } finally {
      setLoading(false);
    }
  }, [region]);

  useEffect(() => {
    loadStops();
  }, [loadStops]);

  const handleStopSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
    const searchQuery = event.target.value || "";

    if (searchTimeout.current) {
      clearTimeout(searchTimeout.current);
    }

    searchTimeout.current = setTimeout(() => {
      if (searchQuery.length === 0) {
        setSearchResults(null);
        return;
      }

      if (!data) {
        console.error("No data available for search");
        return;
      }

      // Check if search query is a number (stop code search)
      const isNumericSearch = /^\d+$/.test(searchQuery.trim());

      let items: Stop[];
      if (isNumericSearch) {
        // Direct match for stop codes
        const stopId = parseInt(searchQuery.trim(), 10);
        const exactMatch = data.filter((stop) => stop.stopId === stopId);
        if (exactMatch.length > 0) {
          items = exactMatch;
        } else {
          // Fuzzy search if no exact match
          const results = fuse.search(searchQuery);
          items = results.map((result) => result.item);
        }
      } else {
        // Text search using Fuse.js
        const results = fuse.search(searchQuery);
        items = results.map((result) => result.item);
      }

      setSearchResults(items);
    }, 300);
  };

  return (
    <div className="stoplist-page">
      <div className="stoplist-section search-container">
        <h3 className="page-subtitle">{t("stoplist.search_label", "Buscar paradas")}</h3>
        <input
          type="search"
          placeholder={randomPlaceholder}
          onChange={handleStopSearch}
          className="search-bar"
        />
      </div>

      {searchResults && searchResults.length > 0 && (
        <div className="stoplist-section list-container">
          <h2 className="page-subtitle">
            {t("stoplist.search_results", "Resultados de la búsqueda")}
          </h2>
          <ul className="list">
            {searchResults.map((stop: Stop) => (
              <StopItem key={stop.stopId} stop={stop} />
            ))}
          </ul>
        </div>
      )}

      {!loading && (
        <StopGallery
          stops={favouriteStops.sort((a, b) => a.stopId - b.stopId)}
          title={t("stoplist.favourites")}
          emptyMessage={t("stoplist.no_favourites")}
        />
      )}

      {!loading && (
        <StopGallery
          stops={recentStops.slice(0, 5)}
          title={t("stoplist.recents")}
        />
      )}

      {/*<ServiceAlerts />*/}

      <div className="stoplist-section list-container">
        <h2 className="page-subtitle">
          {userLocation
            ? t("stoplist.nearby_stops", "Nearby stops")
            : t("stoplist.all_stops", "Paradas")}
        </h2>

        <ul className="list">
          {loading && (
            <>
              {Array.from({ length: 6 }, (_, index) => (
                <StopItemSkeleton key={`skeleton-${index}`} />
              ))}
            </>
          )}
          {!loading && data
            ? (userLocation ? sortedAllStops.slice(0, 6) : sortedAllStops).map(
                (stop) => <StopItem key={stop.stopId} stop={stop} />
              )
            : null}
        </ul>
      </div>
    </div>
  );
}
