import { useEffect, useState } from "preact/hooks";

import { LNP_MD_LINES, LNP_MD_STOPS, LNP_SOURCE_ID } from "../constants";

import useMapContext from "./useMapContext";

import type { VectorTileSource } from "maplibre-gl";

export interface LnpLineInfo {
  color: string;
  extent: [number, number, number, number];
  external_id: string;
  id: string;
  long_name: string;
  mot: string;
  operator_name: string;
  runs: number;
  short_name: string;
  text_color: string;
}

export interface LnpStopInfo {
  codes: string[];
  coordinates: [number, number];
  external_id: string;
  importance: number;
  long_name: string;
  short_name: string;
  tralis_network: string;
  visibility_level: number;
}

export type LinesInfos = Record<string, LnpLineInfo>;
export type StopsInfos = Record<string, LnpStopInfo>;

let cacheLnpSourceInfo: {
  [LNP_MD_LINES]: LinesInfos;
  [LNP_MD_STOPS]: StopsInfos;
} = null;

export function useLnpSourceInfos() {
  const { baseLayer } = useMapContext();
  const [sourceUrl, setSourceUrl] = useState<string>(null);

  useEffect(() => {
    const url =
      baseLayer?.mapLibreMap?.getSource<VectorTileSource>(LNP_SOURCE_ID)?.url;
    const onSourceData = (e) => {
      if (e.sourceId === LNP_SOURCE_ID && e.isSourceLoaded) {
        setSourceUrl(e.source?.url);
        baseLayer?.mapLibreMap?.off("sourcedata", onSourceData);
      }
    };
    if (!url) {
      baseLayer?.mapLibreMap?.on("sourcedata", onSourceData);
    } else {
      setSourceUrl(url);
    }
    return () => {
      baseLayer?.mapLibreMap?.off("sourcedata", onSourceData);
    };
  }, [baseLayer?.mapLibreMap]);

  useEffect(() => {
    const abortController = new AbortController();
    const fetchInfos = async (url: string) => {
      if (!cacheLnpSourceInfo) {
        const response = await fetch(url, { signal: abortController.signal });
        const data = await response.json();
        cacheLnpSourceInfo = data;
      }
    };
    if (sourceUrl) {
      void fetchInfos(sourceUrl);
    }
    return () => {
      abortController?.abort();
    };
  }, [sourceUrl]);

  return cacheLnpSourceInfo;
}

export function useLnpLinesInfos(): LinesInfos {
  const infos = useLnpSourceInfos();
  return infos?.[LNP_MD_LINES];
}

export function useLnpStopsInfos(): StopsInfos {
  const infos = useLnpSourceInfos();
  return infos?.[LNP_MD_STOPS];
}

/**
 * This hook search line informations from lnp data. It takes a string in
 * parameter then it will search if there is a property that exactly match this value.
 */
function useLnpLineInfo(text: string): LnpLineInfo {
  const linesInfos = useLnpLinesInfos();

  if (!linesInfos || !text) {
    return null;
  }

  if (linesInfos[text]) {
    return linesInfos[text];
  }

  return Object.values(linesInfos).find((info) => {
    return ["id", "external_id", "short_name", "long_name"].find((key) => {
      return !!info[key] && info[key].toLowerCase() === text.toLowerCase();
    });
  });
}

/**
 * This hook search line informations from lnp data. It takes a string in
 * parameter then it will search if there is a property that exactly match this value.
 */
export function useLnpStopInfo(text: string): LnpStopInfo {
  const stationsInfos = useLnpStopsInfos();

  if (!stationsInfos || !text) {
    return null;
  }

  if (stationsInfos[text]) {
    return stationsInfos[text];
  }

  return Object.values(stationsInfos).find((info) => {
    return (
      ["id", "external_id", "short_name", "long_name"].find((key) => {
        return !!info[key] && info[key].toLowerCase() === text.toLowerCase();
      }) ||
      info?.codes?.find((code) => {
        return (
          code.toLowerCase() === text.toLowerCase() ||
          code.split(":")[1]?.toLowerCase() === text.toLowerCase()
        );
      })
    );
  });
}

export default useLnpLineInfo;
