import React from "react";
import { matchPath } from "react-router-dom";

import { rulesEngineSelectors } from "@dndbeyond/character-rules-engine/es";

// export interface RouteDefinition {
//     key: string,
//     type: string,
//     path: string,
//     Component: any,
//     checkCanAccess: (routeDef:any, state:any) => boolean,
// }
import config from "~/config";
import {
  BuilderMethod,
  NavigationType,
  RouteKey,
  SectionKey,
} from "~/subApps/builder/constants";
import { ClassChoose } from "~/subApps/builder/routes/ClassChoose";
import { Home } from "~/subApps/builder/routes/Home";
import { SpeciesChoose } from "~/subApps/builder/routes/SpeciesChoose";

import { WhatsNext } from "../../../../subApps/builder/routes/WhatsNext";
import AbilityScoresHelp from "../containers/pages/AbilityScoresHelp";
import AbilityScoresManage from "../containers/pages/AbilityScoresManage";
import ClassHelp from "../containers/pages/ClassHelp";
import ClassesManage from "../containers/pages/ClassesManage";
import DescriptionHelp from "../containers/pages/DescriptionHelp";
import DescriptionManage from "../containers/pages/DescriptionManage";
import EquipmentHelp from "../containers/pages/EquipmentHelp";
import EquipmentManage from "../containers/pages/EquipmentManage";
import HomeHelp from "../containers/pages/HomeHelp";
import SpeciesHelp from "../containers/pages/SpeciesHelp";
import SpeciesManage from "../containers/pages/SpeciesManage";
import { NavigationUtils } from "../utils";

// TODO: remove the use of either `Component` or `getComponent` on the ROUTE_DEFINITIONS and we can get rid of the no-dd-sa rule..

const BASE_PATHNAME = config.basePathname;

export const ROUTE_DEFINITIONS = {
  [RouteKey.HOME_BASIC_INFO]: {
    key: RouteKey.HOME_BASIC_INFO,
    type: NavigationType.Page,
    path: `${BASE_PATHNAME}/:characterId/builder/home/basic`,
    Component: Home,
    // no-dd-sa:typescript-best-practices/no-dupe-keys
    getComponent: () => <Home />,
    checkCanAccess: (routeDef, state) =>
      NavigationUtils.checkStdBuilderPageRequirements(routeDef, state),
  },
  [RouteKey.RACE_CHOOSE]: {
    key: RouteKey.RACE_CHOOSE,
    type: NavigationType.Page,
    path: `${BASE_PATHNAME}/:characterId/builder/species/choose`,
    Component: SpeciesChoose,
    getComponent: () => <SpeciesChoose />,
    // no-dd-sa:typescript-best-practices/no-dupe-keys
    checkCanAccess: (routeDef, state) => {
      if (!NavigationUtils.checkStdBuilderPageRequirements(routeDef, state)) {
        return false;
      }

      if (rulesEngineSelectors.getRace(state) !== null) {
        return false;
      }

      return true;
    },
  },
  [RouteKey.RACE_MANAGE]: {
    key: RouteKey.RACE_MANAGE,
    type: NavigationType.Page,
    path: `${BASE_PATHNAME}/:characterId/builder/species/manage`,
    Component: SpeciesManage,
    getComponent: () => <SpeciesManage />,
    // no-dd-sa:typescript-best-practices/no-dupe-keys
    checkCanAccess: (routeDef, state) => {
      if (!NavigationUtils.checkStdBuilderPageRequirements(routeDef, state)) {
        return false;
      }

      if (rulesEngineSelectors.getRace(state) === null) {
        return false;
      }

      return true;
    },
  },
  [RouteKey.CLASS_CHOOSE]: {
    key: RouteKey.CLASS_CHOOSE,
    type: NavigationType.Page,
    path: `${BASE_PATHNAME}/:characterId/builder/class/choose`,
    Component: ClassChoose,
    getComponent: () => <ClassChoose />,
    // no-dd-sa:typescript-best-practices/no-dupe-keys
    checkCanAccess: (routeDef, state) => {
      if (!NavigationUtils.checkStdBuilderPageRequirements(routeDef, state)) {
        return false;
      }

      if (rulesEngineSelectors.getClasses(state).length > 0) {
        return false;
      }

      return true;
    },
  },
  [RouteKey.CLASS_MANAGE]: {
    key: RouteKey.CLASS_MANAGE,
    type: NavigationType.Page,
    path: `${BASE_PATHNAME}/:characterId/builder/class/manage`,
    Component: ClassesManage,
    getComponent: () => <ClassesManage />,
    // no-dd-sa:typescript-best-practices/no-dupe-keys
    checkCanAccess: (routeDef, state) => {
      if (!NavigationUtils.checkStdBuilderPageRequirements(routeDef, state)) {
        return false;
      }

      if (rulesEngineSelectors.getClasses(state).length === 0) {
        return false;
      }

      return true;
    },
  },
  [RouteKey.ABILITY_SCORES_MANAGE]: {
    key: RouteKey.ABILITY_SCORES_MANAGE,
    type: NavigationType.Page,
    path: `${BASE_PATHNAME}/:characterId/builder/ability-scores/manage`,
    Component: AbilityScoresManage,
    // no-dd-sa:typescript-best-practices/no-dupe-keys
    getComponent: () => <AbilityScoresManage />,
    checkCanAccess: (routeDef, state) =>
      NavigationUtils.checkStdBuilderPageRequirements(routeDef, state),
  },
  [RouteKey.DESCRIPTION_MANAGE]: {
    key: RouteKey.DESCRIPTION_MANAGE,
    type: NavigationType.Page,
    path: `${BASE_PATHNAME}/:characterId/builder/description/manage`,
    Component: DescriptionManage,
    // no-dd-sa:typescript-best-practices/no-dupe-keys
    getComponent: () => <DescriptionManage />,
    checkCanAccess: (routeDef, state) =>
      NavigationUtils.checkStdBuilderPageRequirements(routeDef, state),
  },
  [RouteKey.EQUIPMENT_MANAGE]: {
    key: RouteKey.EQUIPMENT_MANAGE,
    type: NavigationType.Page,
    path: `${BASE_PATHNAME}/:characterId/builder/equipment/manage`,
    Component: EquipmentManage,
    // no-dd-sa:typescript-best-practices/no-dupe-keys
    getComponent: () => <EquipmentManage />,
    checkCanAccess: (routeDef, state) =>
      NavigationUtils.checkStdBuilderPageRequirements(routeDef, state),
  },

  [RouteKey.WHATS_NEXT]: {
    key: RouteKey.WHATS_NEXT,
    type: NavigationType.Page,
    path: `${BASE_PATHNAME}/:characterId/builder/whats-next`,
    Component: WhatsNext,
    // no-dd-sa:typescript-best-practices/no-dupe-keys
    getComponent: () => <WhatsNext />,
    checkCanAccess: (routeDef, state) =>
      NavigationUtils.checkStdBuilderPageRequirements(routeDef, state),
  },

  [RouteKey.HOME_HELP]: {
    key: RouteKey.HOME_HELP,
    type: NavigationType.HelpPage,
    path: `${BASE_PATHNAME}/:characterId/builder/home/help`,
    Component: HomeHelp,
    // no-dd-sa:typescript-best-practices/no-dupe-keys
    getComponent: () => <HomeHelp />,
    checkCanAccess: (routeDef, state) =>
      NavigationUtils.checkStdBuilderPageRequirements(routeDef, state),
  },
  [RouteKey.RACE_HELP]: {
    key: RouteKey.RACE_HELP,
    type: NavigationType.HelpPage,
    path: `${BASE_PATHNAME}/:characterId/builder/species/help`,
    Component: SpeciesHelp,
    // no-dd-sa:typescript-best-practices/no-dupe-keys
    getComponent: () => <SpeciesHelp />,
    checkCanAccess: (routeDef, state) =>
      NavigationUtils.checkStdBuilderPageRequirements(routeDef, state),
  },
  [RouteKey.CLASS_HELP]: {
    key: RouteKey.CLASS_HELP,
    type: NavigationType.HelpPage,
    path: `${BASE_PATHNAME}/:characterId/builder/class/help`,
    Component: ClassHelp,
    // no-dd-sa:typescript-best-practices/no-dupe-keys
    getComponent: () => <ClassHelp />,
    checkCanAccess: (routeDef, state) =>
      NavigationUtils.checkStdBuilderPageRequirements(routeDef, state),
  },
  [RouteKey.ABILITY_SCORES_HELP]: {
    key: RouteKey.ABILITY_SCORES_HELP,
    type: NavigationType.HelpPage,
    path: `${BASE_PATHNAME}/:characterId/builder/ability-scores/help`,
    Component: AbilityScoresHelp,
    // no-dd-sa:typescript-best-practices/no-dupe-keys
    getComponent: () => <AbilityScoresHelp />,
    checkCanAccess: (routeDef, state) =>
      NavigationUtils.checkStdBuilderPageRequirements(routeDef, state),
  },
  [RouteKey.DESCRIPTION_HELP]: {
    key: RouteKey.DESCRIPTION_HELP,
    type: NavigationType.HelpPage,
    path: `${BASE_PATHNAME}/:characterId/builder/description/help`,
    Component: DescriptionHelp,
    // no-dd-sa:typescript-best-practices/no-dupe-keys
    getComponent: () => <DescriptionHelp />,
    checkCanAccess: (routeDef, state) =>
      NavigationUtils.checkStdBuilderPageRequirements(routeDef, state),
  },
  [RouteKey.EQUIPMENT_HELP]: {
    key: RouteKey.EQUIPMENT_HELP,
    type: NavigationType.HelpPage,
    path: `${BASE_PATHNAME}/:characterId/builder/equipment/help`,
    Component: EquipmentHelp,
    // no-dd-sa:typescript-best-practices/no-dupe-keys
    getComponent: () => <EquipmentHelp />,
    checkCanAccess: (routeDef, state) =>
      NavigationUtils.checkStdBuilderPageRequirements(routeDef, state),
  },
};

export interface RouteDef {
  key: string; // get the thing as an enum
  type: string;
  path: string;
  Component: React.ComponentType<any>;
  // no-dd-sa:typescript-best-practices/no-dupe-keys
  getComponent: () => React.ReactElement;
  checkCanAccess?: (routeDef: RouteDef, state: any) => boolean;
}

export const getAllRouterRoutes = () => {
  return (Object.values(ROUTE_DEFINITIONS) as RouteDef[]).map((routeDef) => ({
    element: <routeDef.Component />,
    path: routeDef.path,
    routeDef,
  }));
};

const addSearchParamsToRouteDef = (routeDef: RouteDef): RouteDef => {
  let params = "";
  if (typeof window !== "undefined") {
    // If we ever need to know specific search params, we can use URLSearchParams here instead.
    params = window.location.search;
  }
  return {
    ...routeDef,
    path: `${routeDef.path}${params}`,
  };
};

export const getRouteDef = (routeKey) => ROUTE_DEFINITIONS[routeKey];
export const getRouteDefPath = (routeKey) => {
  const basePath = getRouteDef(routeKey);
  return addSearchParamsToRouteDef(basePath).path;
};
export const getRouteDefType = (routeKey) => getRouteDef(routeKey).type;

export const NAVIGATION_DEFINITIONS = {
  [BuilderMethod.STEP_BY_STEP]: [
    {
      type: NavigationType.Section,
      key: SectionKey.HOME,
      getLabel: () => "Home",
      routes: [
        getRouteDef(RouteKey.HOME_HELP),
        getRouteDef(RouteKey.HOME_BASIC_INFO),
      ],
    },
    {
      type: NavigationType.Section,
      key: SectionKey.CLASS,
      getLabel: () => "1. Class",
      routes: [
        getRouteDef(RouteKey.CLASS_HELP),
        getRouteDef(RouteKey.CLASS_CHOOSE),
        getRouteDef(RouteKey.CLASS_MANAGE),
      ],
    },
    {
      type: NavigationType.Section,
      key: SectionKey.DESCRIPTION,
      getLabel: () => "2. Background",
      routes: [
        getRouteDef(RouteKey.DESCRIPTION_HELP),
        getRouteDef(RouteKey.DESCRIPTION_MANAGE),
      ],
    },
    {
      type: NavigationType.Section,
      key: SectionKey.RACE,
      getLabel: () => "3. Species",
      routes: [
        getRouteDef(RouteKey.RACE_HELP),
        getRouteDef(RouteKey.RACE_CHOOSE),
        getRouteDef(RouteKey.RACE_MANAGE),
      ],
    },
    {
      type: NavigationType.Section,
      key: SectionKey.ABILITY_SCORES,
      getLabel: () => "4. Abilities",
      routes: [
        getRouteDef(RouteKey.ABILITY_SCORES_HELP),
        getRouteDef(RouteKey.ABILITY_SCORES_MANAGE),
      ],
    },
    {
      type: NavigationType.Section,
      key: SectionKey.EQUIPMENT,
      getLabel: () => "5. Equipment",
      routes: [
        getRouteDef(RouteKey.EQUIPMENT_HELP),
        getRouteDef(RouteKey.EQUIPMENT_MANAGE),
      ],
    },
    {
      type: NavigationType.Section,
      key: SectionKey.WHATS_NEXT,
      getLabel: () => "What's Next",
      routes: [getRouteDef(RouteKey.WHATS_NEXT)],
    },
  ],
};

export function checkIsRouteInSection(testRouteDef, sectionRoutes) {
  if (testRouteDef && sectionRoutes) {
    return !!sectionRoutes.filter(
      (routeDef) => routeDef.key === testRouteDef.key
    ).length;
  }

  return false;
}

export function getNavigationSections(builderMethod, currentRouteDef) {
  const navConfig = getNavBuilderConfig(builderMethod);

  return navConfig.map((section) => ({
    ...section,
    active: checkIsRouteInSection(currentRouteDef, section.routes),
  }));
}

export function getNavBuilderConfig(builderMethod) {
  if (builderMethod) {
    const config = NAVIGATION_DEFINITIONS[builderMethod];

    // Add search params to all route paths in the config
    return config.map((section) => ({
      ...section,
      routes: section.routes.map((routeDef) =>
        addSearchParamsToRouteDef(routeDef)
      ),
    }));
  } else {
    return [];
  }
}

export function getCurrentRouteDef(currentPath) {
  const routes = getAllRouterRoutes();

  let matchedRouteDef: RouteDef | null = null;
  routes.forEach((route) => {
    const match = matchPath(route.path, currentPath);

    if (match) {
      matchedRouteDef = route.routeDef;
    }
  });

  return matchedRouteDef;
}
