import React, { useEffect } from "react";
import Modal from "react-modal";
import { useStore } from "react-redux";
import {
  useLocation,
  useMatch,
  useNavigate,
  useSearchParams,
} from "react-router-dom";

import {
  characterEnvActions,
  ConfigUtils,
  Constants,
  generateApiAdapter,
} from "@dndbeyond/character-rules-engine/es";

import config from "~/config";
import { useAuth } from "~/contexts/Authentication";
import { CharacterThemeProvider } from "~/contexts/CharacterTheme";
import { SidebarProvider } from "~/contexts/Sidebar";
import useUserName from "~/hooks/useUserName";
import useUserRoles from "~/hooks/useUserRoles";

import "../styles/styles.scss";
import CharacterBuilderContainer from "./CharacterBuilder/containers/CharacterBuilderContainer";
import CharacterSheetContainer from "./CharacterSheet/containers/CharacterSheetContainer";
import { appEnvActions } from "./Shared/actions";
import { Managers } from "./Shared/managers";
import {
  DiceFeatureConfigurationState,
  GameLogState,
} from "./Shared/stores/typings";
import {
  AppLoggerUtils,
  AppNotificationUtils,
  ErrorUtils,
} from "./Shared/utils";
import appConfig from "./config";

const BASE_PATHNAME = config.basePathname;

interface Props {
  readOnly: boolean;
  redirect?: string;
  characterId: number | null;
}

export const SheetBuilderApp: React.FC<Props> = ({
  readOnly,
  characterId,
  redirect,
}) => {
  const username = useUserName();
  const user = useAuth();
  const userRoles = useUserRoles();
  const navigate = useNavigate();
  const matchPremadePage = useMatch(`${BASE_PATHNAME}/premade/*`);
  const matchBuilderWithOutCharacter = useMatch(`${BASE_PATHNAME}/builder/*`);
  const matchBuilderWithCharacter = useMatch(
    `${BASE_PATHNAME}/:characterId/builder/*`
  );
  const matchSheet = useMatch(`${BASE_PATHNAME}/:characterId/`);
  const matchSheetWithShareId = useMatch(
    `${BASE_PATHNAME}/:characterId/:shareId`
  );
  const currentPath = useLocation().pathname;
  const [searchParams] = useSearchParams();
  const isVttView = searchParams.get("view") === "vtt";

  if (config === undefined) {
    throw Error("Missing Config");
  }

  let appContext: Constants.AppContextTypeEnum | null = null;
  switch (true) {
    case !!(
      matchBuilderWithOutCharacter ||
      matchBuilderWithCharacter ||
      matchPremadePage
    ):
      appContext = Constants.AppContextTypeEnum.BUILDER;
      break;
    case !!(matchSheet || matchSheetWithShareId):
      appContext = Constants.AppContextTypeEnum.SHEET;
      break;
    default:
      throw new Error("Invalid app context");
  }

  const diceFeatureConfiguration: DiceFeatureConfigurationState = {
    enabled: true,
    menu: true,
    assetBaseLocation: config.diceAssetEndpoint,
    apiEndpoint: config.diceApiEndpoint,
    trackingId: config.analyticTrackingId,
  };

  ErrorUtils.initReporting({
    debug: appConfig.debug,
  });

  const store = useStore();
  const initializedRef = React.useRef(false);

  let parsedDiceEnabled: boolean = true;
  if (!diceFeatureConfiguration.enabled) {
    parsedDiceEnabled = false;
  } else {
    // if local storage is disabled it will just crash when trying to access, this prevents crashing.
    try {
      const diceEnabled = localStorage.getItem("dice-enabled");
      // once dice are enabled set the default to on;
      parsedDiceEnabled = diceEnabled === "true" || diceEnabled === null;
    } catch (e) {}
  }

  let lastMessageTime = 0;
  try {
    const lsLastMessageTime = localStorage.getItem(
      `gameLog-lastMessageTime-${characterId}`
    );
    if (lsLastMessageTime) {
      lastMessageTime = parseInt(lsLastMessageTime);
    }
  } catch (e) {}

  const gameLogSettings: GameLogState = {
    isOpen: false,
    lastMessageTime: lastMessageTime,
    apiEndpoint: config.gameLogApiEndpoint,
    ddbApiEndpoint: config.ddbApiEndpoint,
  };

  ConfigUtils.configureRulesEngine({
    apiAdapter: generateApiAdapter(config.authEndpoint, readOnly),
    apiInfo: config.apiInfo,
    store,
    onLogMessage: AppLoggerUtils.logMessage,
    onLogError: AppLoggerUtils.logError,
    onNotification: AppNotificationUtils.dispatchNotification,
    includeCustomItems: true,
    canUseCurrencyContainers: true,
  });

  // CharacterSheet.componentDidMount fires before parent useEffects, so characterId and
  // appContext must be in the store synchronously before children mount. Without this,
  // the character load saga reads a null characterId and the character fails to load.
  // The useEffect below handles re-dispatching once username/user resolve asynchronously.
  if (!initializedRef.current) {
    initializedRef.current = true;
    store.dispatch(
      characterEnvActions.dataSet({
        context: appContext,
      })
    );
    store.dispatch(
      appEnvActions.dataSet({
        username,
        userId: user?.id ? Number(user.id) : -1,
        userRoles,
        characterId,
        authEndpoint: config.authEndpoint,
        redirect,
        diceEnabled: parsedDiceEnabled,
        diceFeatureConfiguration: diceFeatureConfiguration,
        gameLog: gameLogSettings,
      })
    );
  }

  useEffect(() => {
    store.dispatch({
      type: "SET_ROUTE_CONTROLS",
      payload: { navigate, currentPath },
    });
  }, [navigate, currentPath]);

  useEffect(() => {
    store.dispatch(
      appEnvActions.dataSet({
        username,
        userId: user?.id ? Number(user.id) : -1,
        userRoles,
        characterId,
        authEndpoint: config.authEndpoint,
        redirect,
        diceEnabled: parsedDiceEnabled,
        diceFeatureConfiguration: diceFeatureConfiguration,
        gameLog: gameLogSettings,
      })
    );
  }, [username, user]);

  // Setup axe-core in development, not in production
  if (process.env.NODE_ENV !== "production") {
    // axe(React, ReactDOM, 10000);
  }

  Modal.setAppElement("#character-tools-target");

  // If the view is the VTT view, set --top-navigation-height to 0 because the top nav will not be present.
  useEffect(() => {
    if (isVttView) {
      document.body.style.setProperty("--top-navigation-height", "0px");
    }
  }, []);

  return (
    <Managers>
      <CharacterThemeProvider>
        <Content />
      </CharacterThemeProvider>
    </Managers>
  );
};

const Content = () => {
  const matchBuilderWithOutCharacter = useMatch(`${BASE_PATHNAME}/builder/*`);
  const matchBuilderWithCharacter = useMatch(
    `${BASE_PATHNAME}/:characterId/builder/*`
  );
  const matchPremadePage = useMatch(`${BASE_PATHNAME}/premade/*`);

  return (
    <SidebarProvider>
      {matchBuilderWithOutCharacter ||
      matchBuilderWithCharacter ||
      matchPremadePage ? (
        <CharacterBuilderContainer isPremadeListing={!!matchPremadePage} />
      ) : (
        <CharacterSheetContainer authEndpoint={config.authEndpoint} />
      )}
    </SidebarProvider>
  );
};
