import React from "react";
import { Route, Routes } from "react-router-dom";

import Stack from "@mui/material/Stack";

import { OpenAPI } from "openapi-types";

import ErrorScreen from "../containers/ErrorScreen";
import PageErrorBoundary from "../containers/PageErrorBoundary";
import PageLoader from "../containers/PageLoader";
import { RouteInfo, useRouter } from "../contexts/RouterContext";
import DashboardPage from "../pages/DashboardPage";
import { PageComponent } from "../types";

export type RouterExtensionPageHit =
  | PageComponent
  | Promise<PageComponent>
  | {
      page: PageComponent | Promise<PageComponent>;
      request?: OpenAPI.Request;
      defaultRequest?: OpenAPI.Request;
      offline?: boolean;
    };

export interface RouterExtension {
  app: string;
  pages: (route: RouteInfo) => RouterExtensionPageHit | undefined;
}

export interface RouterProps {
  dashboard?: React.ComponentType;
  extensions?: RouterExtension[];
}

export const Router: React.FC<RouterProps> = ({ dashboard, extensions }) => {
  extensions ??= Array.isArray(extensions) ? extensions : [];

  const { routes } = useRouter();

  const Dashboard = dashboard ?? DashboardPage;

  const extensionsMap = new Map(extensions.map(({ app, pages }) => [app, pages]));

  const pageRoutes = routes.map((route) => {
    const hit = extensionsMap.get(route.app)?.(route);
    if (hit == null) return;

    const page = "page" in hit ? hit.page : hit;
    const request = "request" in hit ? hit.request : undefined;
    const defaultRequest = "defaultRequest" in hit ? hit.defaultRequest : undefined;
    const offline = "offline" in hit ? hit.offline : undefined;

    const Element = () => (
      <PageErrorBoundary errorPage={ErrorScreen}>
        <PageLoader
          defaultRequest={defaultRequest}
          offline={offline}
          page={page}
          request={request}
          route={route}
        />
      </PageErrorBoundary>
    );

    return <Route key={route.path} element={<Element />} path={route.path} />;
  });

  return (
    <Stack sx={{ width: "100%", minHeight: "100%" }}>
      <Routes>
        <Route index element={<Dashboard />} />
        {pageRoutes}
      </Routes>
    </Stack>
  );
};
