import React, {useEffect, useState} from 'react';
import PanelWrapper from "../../meta/PanelWrapper";
import PlanElement from "./components/PlanElement";
import PanelTitle from "../../meta/PanelTitle";
import PanelContent from "../../meta/PanelContent";
import {StationResponse} from "./types/vrrfAPI";
import {Warning} from "@phosphor-icons/react";
import {motion} from 'framer-motion';

export type FahrplanPanelDefinition = {
  stops: string[],
  filter: {
    types?: string[],
    destinations?: string[]
  }
}

type Route = {
  uid: string,
  identifier: string,
  heading: string,
  stops: {
    name: string,
    arrival: Date,
    delay?: number,
    countdown: number,
    cancelled: boolean
  }[],
  countdown: number
}

const FahrplanPanel = (props: {definition: FahrplanPanelDefinition}) => {
  const [routes, setRoutes] = useState<Route[]>([]);

  useEffect(() => {
    const update = async () => {
      const departures = (await Promise.all(props.definition.stops.map(getStopData)))
        .map(d => d.raw)
        .flat();

      let newRoutes: Route[] = [];

      // Determine stop data from the departures
      for(let departure of departures) {
        // First throw away all data that belongs to a filtered category
        const filterTypes = props.definition.filter.types ?? [];
        if(filterTypes.includes(departure.type)) {
          continue;
        }

        // Find existing route with same uid
        const existing_ind = newRoutes.findIndex(r => r.uid === departure.key + "-" + departure.lineref.identifier)

        // Pre-compute values that will be needed regardless
        const delay = stringToDelay(departure.delay);
        const arrival = processArrival(departure.sched_date, departure.time);

        // Throw away stops that are five hours in the future as keys start colliding at some point
        if(arrival.getTime() >= 5 * 60 * 60 * 1000 + (new Date()).getTime()) {
          continue;
        }

        if(existing_ind === -1) {
          // If it does not exist, create a new route
          newRoutes.push({
            uid: departure.key + "-" + departure.lineref.identifier,
            heading: departure.destination,
            identifier: departure.line,
            stops: [
              {
                name: departure.internal.stop,
                arrival,
                delay,
                countdown: parseInt(departure.countdown),
                cancelled: departure.is_cancelled === 1
              }
            ],
            countdown: parseInt(departure.countdown)
          })
        } else {
          // If it does, just add a stop to the existing route
          newRoutes[existing_ind].stops.push({
            name: departure.internal.stop,
            arrival,
            delay: stringToDelay(departure.delay),
            countdown: parseInt(departure.countdown),
            cancelled: departure.is_cancelled === 1
          })

          newRoutes[existing_ind].stops = newRoutes[existing_ind].stops
            .sort((a, b) => a.countdown - b.countdown)
        }
      }

      // Sort the output
      newRoutes = newRoutes.sort((a, b) => {
        const diff = a.stops[0].arrival.getTime() - b.stops[0].arrival.getTime();

        if(diff !== 0) {
          return diff;
        }

        const latestA = Math.max(...a.stops.map(s => s.arrival.getTime()));
        const latestB = Math.max(...b.stops.map(s => s.arrival.getTime()));

        return latestA - latestB;
      })

      // Write to the display
      setRoutes(newRoutes);
    }

    update();
    const interval = setInterval(update, 2 * 60 * 1000);

    return () => {
      clearInterval(interval);
    }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <PanelWrapper>
      <PanelTitle title={"ÖPNV Monitor"}/>
      <PanelContent className={"flex flex-col"}>
        <motion.div layout transition={{duration: .3, ease: "easeOut"}} className={"flex-1 flex flex-col gap-3"}>
          {routes.map((route) => (
            <PlanElement
              key={route.uid}
              tripId={route.uid}
              trainIdentifier={route.identifier}
              trainHeading={route.heading}
              stops={route.stops}
            />
          ))}

          {routes.length === 0 && (
            <div className={"flex-1 flex justify-center items-center"}>
              <div className={"mb-10 flex flex-col items-center"}>
                <Warning size={48} className={"mb-3"}/>
                <p className={"max-w-xs text-center text-zinc-400"}>
                  Aktuell sind keine Abfahrtsdaten verfügbar.
                </p>
              </div>
            </div>
          )}
        </motion.div>
      </PanelContent>
    </PanelWrapper>


  );
};

export default FahrplanPanel;

async function getStopData(stop: string): Promise<StationResponse> {
  const request = await fetch(`https://vrrf.finalrewind.org/${encodeURIComponent(stop)}.json`);
  if(!request.ok) {
    throw new Error("Converting stop did not work");
  }
  const data = await request.json();

  if(data.error) {
    console.warn("Stop data for", stop, "could not be fetched");
  }

  // Add internal reference data
  data.raw = data.raw.map((r: any) => ({
    ...r,
    internal: {
      stop
    }
  }))

  console.log(data);

  return data as StationResponse;
}

function stringToDelay(input: string): number | undefined {
  const delay = parseInt(input);
  if(delay === 0) {
    return undefined;
  }
  if(isNaN(delay)) {
    console.warn("While parsing delay, the string was not interpretable as number", input);
    return undefined;
  }
  return delay;
}

function processArrival(date: string, time: string): Date {
  const d_parts = date.split(".");
  return new Date(`${d_parts[2]}-${d_parts[1]}-${d_parts[0]} ${time}`);
}