import Head from 'next/head'
import { Service, Status, ServiceType, ServiceLocation } from '../interfaces/CardTypes';
import Dockerode from 'dockerode';
import { ReactElement } from 'react'
import useSWR from 'swr';
import { CardContentService, PageContentBox, PageDescription, PageTitle } from '../components/styles/content';
import ServiceList from '../public/pages.json';

const fetcher = (url: string) => fetch(url).then((res) => res.json())

function Services() {
  const { initialData, fullData, loadingFull, error } = useServices();

  let content: ReactElement = <></>;

  if (error) { content = <div>Error loading data</div> }
  else if (loadingFull) {
    content =
      <PageContentBox>
        {initialData?.map((item: Service) => (
          <CardContentService key={item.name} content={item} />
        ))}
      </PageContentBox>
  }
  else if (fullData) {
    content =
      <PageContentBox>
        {fullData.map((item: Service) => (
          <CardContentService key={item.name} content={item} />
        ))}
      </PageContentBox>
  }
  else {
    content = <div>Error loading data</div>
  }

  return (
    <>
      <Head>
        <title>Neshura Servers</title>
        <meta charSet='utf-8' />
        <link rel="icon" href="/favicon.ico" />
        <meta name="description" content="Lists all available Services, most likely up-to-date" />
      </Head>

      <PageTitle>
        Service List
      </PageTitle>

      <PageDescription>
        Lists all available Services, most likely up-to-date
      </PageDescription>

      {content}
    </>
  )
}

async function getStatus(entry: Service, containers: Dockerode.ContainerInfo[]) {
  // Currently the only location supporting different fetching depending on type is brr7-4800u
  // Others to follow but low prio as this is currently the only location used

  // Location BRR7-4800U
  if (entry.location === ServiceLocation.brr7_4800u) {
    // Type APP
    if (entry.type === ServiceType.app) {
      await fetch(entry.href)
        .then((response) => {
          if (response.ok) {
            switch (response.status) {
              case 200:
              case 301:
              case 302:
                entry.status = Status.online;
                break;
              default:
                entry.status = Status.offline;
            }
          }
          else {
            entry.status = Status.offline;
          }
        })
        .catch((error) => {
          console.error("Error pinging Website: ", error);
          entry.status = Status.error;
        })
    }
    // Type Docker
    else if (entry.type === ServiceType.docker) {
      if (entry.name !== null) {
        let found = false;
        for (let i = 0; i < containers.length; i++) {
          const container = containers[i];
          // Docker API returns container names with / prepended
          if (containers[i].Names.includes("/" + entry.docker_container_name)) {
            // so far only "running" is properly implemented, mroe cases to follow as needed
            switch (container.State) {
              case "running":
                entry.status = Status.online;
                break;
              default:
                console.log("Container Status " + container.State + " has no case implemented");
                entry.status = Status.offline;
            }
            found = true;
            // cancel the for
            break;
          }
          // If container name is not missing the container is set to offline
          else {
            entry.status = Status.offline;
          }
        }
        if (!found) {
          console.warn("Container for " + entry.name + " could not be found");
        }
      }
      // if name is null do not enter for loop
      else {
        console.error("Container Name not specified");
        entry.status = Status.error;
      }
    }
    // If no Type matches
    else {
      console.warn("Service Type for Service " + entry.name + " not specified or invalid");
      entry.status = Status.error;
    }
  }
  // Location Other
  // TODO: implement docker type for other locations
  else if (entry.location === ServiceLocation.other) {
    // Currently uses the same handling as app type for the other location
    await fetch(entry.href)
      .then((response) => {
        if (response.ok) {
          switch (response.status) {
            case 200:
            case 301:
            case 302:
              entry.status = Status.online;
              break;
            default:
              entry.status = Status.offline;
          }
        }
        else {
          entry.status = Status.offline;
        }
      })
      .catch((error) => {
        console.error("Error pinging Website: ", error);
        entry.status = Status.error;
      })
  }
  // If no Location matches
  else {
    console.warn("Service Location for Service " + entry.name + " not specified");
    entry.status = Status.error;
  }
  return entry;
}

const fetchFullDataArray = (containerData: Dockerode.ContainerInfo[], dataSet: Service[]) => {
  const fetchStatus = (entry: Service) => getStatus(entry, containerData);
  return Promise.all(dataSet.map(fetchStatus));
}

function useServices() {
  const { data: containerData, error: containerError } = useSWR('/api/containers', fetcher);
  // TODO: unfuck this
  const initialData: Service[] = JSON.parse(JSON.stringify(ServiceList.services));
  initialData.forEach((service) => {
    if (service.status === undefined) service.status = Status.loading;
  })
  const { data: fullData, error: fullError } = useSWR((containerData) ? [containerData, initialData] : null, fetchFullDataArray)
  const loadingFull = !fullData && !fullError
  return {
    initialData,
    fullData,
    loadingFull,
    error: fullError || containerError,
  };
}

export default Services