/**
 * Copyright (c) 2020-present, Goldman Sachs
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import React, { Suspense, useEffect } from 'react';
import { observer } from 'mobx-react-lite';
import {
  CubesLoadingIndicator,
  CubesLoadingIndicatorIcon,
  GhostIcon,
} from '@finos/legend-art';
import {
  useApplicationStore,
  LegendTokenSync,
} from '@finos/legend-application';
import {
  BrowserEnvironmentProvider,
  Outlet,
  Route,
  Routes,
  Navigate,
} from '@finos/legend-application/browser';
import {
  LegendMarketplaceFrameworkProvider,
  useLegendMarketplaceApplicationStore,
  useLegendMarketplaceBaseStore,
} from './providers/LegendMarketplaceFrameworkProvider.js';
import { LEGEND_MARKETPLACE_ROUTE_PATTERN } from '../__lib__/LegendMarketplaceNavigation.js';
import {
  type AuthProviderProps,
  AuthProvider,
  withAuthenticationRequired,
} from 'react-oidc-context';
import type { User } from 'oidc-client-ts';
import type { LegendMarketplaceOidcConfig } from './LegendMarketplaceApplicationConfig.js';
import { MarketplaceLakehouseHeader } from '../components/Header/LegendMarketplaceHeader.js';
import { LegendMarketplacePage } from '../pages/LegendMarketplacePage.js';
import { CartToast } from '../components/Toast/CartToast.js';
import { FeedbackWidget } from '../components/Feedback/FeedbackWidget.js';
import { flowResult } from 'mobx';

// Lazy load page components for code splitting
const MarketplaceLakehouseHome = React.lazy(() =>
  import('../pages/Lakehouse/MarketplaceLakehouseHome.js').then((module) => ({
    default: module.MarketplaceLakehouseHome,
  })),
);
const LakehouseDataProduct = React.lazy(() =>
  import('../pages/Lakehouse/dataProduct/LakehouseDataProduct.js').then(
    (module) => ({
      default: module.LakehouseDataProduct,
    }),
  ),
);
const TerminalProduct = React.lazy(() =>
  import('../pages/Lakehouse/dataProduct/TerminalProduct.js').then(
    (module) => ({
      default: module.TerminalProduct,
    }),
  ),
);
const LegendMarketplaceDataAPIs = React.lazy(() =>
  import('../pages/DataAPIs/LegendMarketplaceDataAPIs.js').then((module) => ({
    default: module.LegendMarketplaceDataAPIs,
  })),
);
const LakehouseEntitlements = React.lazy(() =>
  import('../pages/Lakehouse/entitlements/LakehouseEntitlements.js').then(
    (module) => ({
      default: module.LakehouseEntitlements,
    }),
  ),
);
const LakehouseDataContractTask = React.lazy(() =>
  import('../pages/Lakehouse/entitlements/LakehouseDataContract.js').then(
    (module) => ({
      default: module.LakehouseDataContractTask,
    }),
  ),
);
const WorkflowDataAccessRequestTask = React.lazy(() =>
  import('../pages/Lakehouse/entitlements/WorkflowDataAccessRequest.js').then(
    (module) => ({
      default: module.WorkflowDataAccessRequestTask,
    }),
  ),
);
const PermitDataAccessRequestTask = React.lazy(() =>
  import('../pages/Lakehouse/entitlements/PermitDataAccessRequest.js').then(
    (module) => ({
      default: module.PermitDataAccessRequestTask,
    }),
  ),
);
const LakehouseAdmin = React.lazy(() =>
  import('../pages/Lakehouse/admin/LakehouseAdmin.js').then((module) => ({
    default: module.LakehouseAdmin,
  })),
);
const MarketplaceLakehouseOAuthCallback = React.lazy(() =>
  import('../pages/Lakehouse/MarketplaceLakehouseOAuthCallback.js').then(
    (module) => ({
      default: module.MarketplaceLakehouseOAuthCallback,
    }),
  ),
);
const LakehouseSDLCDataProduct = React.lazy(() =>
  import('../pages/Lakehouse/dataProduct/LakehouseSDLCDataProduct.js').then(
    (module) => ({
      default: module.LakehouseSDLCDataProduct,
    }),
  ),
);
const LegendMarketplaceSearchResults = React.lazy(() =>
  import(
    '../pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.js'
  ).then((module) => ({
    default: module.LegendMarketplaceSearchResults,
  })),
);
const LegendMarketplaceFieldSearchResults = React.lazy(() =>
  import(
    '../pages/Lakehouse/searchResults/LegendMarketplaceFieldSearchResults.js'
  ).then((module) => ({
    default: module.LegendMarketplaceFieldSearchResults,
  })),
);
const LegacyDataProduct = React.lazy(() =>
  import('../pages/Lakehouse/dataProduct/LegacyDataProduct.js').then(
    (module) => ({
      default: module.LegacyDataProduct,
    }),
  ),
);
const LegendMarketplaceAgents = React.lazy(() =>
  import('../pages/Agents/LegendMarketplaceAgents.js').then((module) => ({
    default: module.LegendMarketplaceAgents,
  })),
);
const LegendMarketplaceInventory = React.lazy(() =>
  import('../pages/Inventory/LegendMarketplaceInventory.js').then((module) => ({
    default: module.LegendMarketplaceInventory,
  })),
);
const LegendMarketplaceVendorData = React.lazy(() =>
  import('../pages/TerminalsAddons/LegendMarketplaceTerminalsAddons.js').then(
    (module) => ({
      default: module.LegendMarketplaceVendorData,
    }),
  ),
);
const LegendMarketplaceSubscriptions = React.lazy(() =>
  import('../pages/Profile/LegendMarketplaceSubscriptions.js').then(
    (module) => ({
      default: module.LegendMarketplaceSubscriptions,
    }),
  ),
);
const LegendMarketplaceYourOrders = React.lazy(() =>
  import('../pages/Profile/LegendMarketplaceYourOrders.js').then((module) => ({
    default: module.LegendMarketplaceYourOrders,
  })),
);

const NotFoundPage = observer(() => {
  const applicationStore = useApplicationStore();

  const currentPath =
    applicationStore.navigationService.navigator.getCurrentLocation();

  return (
    <LegendMarketplacePage className="legend-marketplace__not-found">
      <div className="not-found-screen not-found-screen--no-documentation">
        <div className="not-found-screen__icon">
          <div className="not-found-screen__icon__ghost">
            <GhostIcon />
          </div>
          <div className="not-found-screen__icon__shadow">
            <svg viewBox="0 0 600 400">
              <g transform="translate(300 200)">
                <ellipse
                  className="not-found-screen__icon__shadow__inner"
                  rx="320"
                  ry="80"
                ></ellipse>
              </g>
            </svg>
          </div>
        </div>
        <div className="not-found-screen__text-content">
          <div className="not-found-screen__text-content__title">
            404. Not Found
          </div>
          <div className="not-found-screen__text-content__detail">
            The requested URL
            <span className="not-found-screen__text-content__detail__url">
              {applicationStore.navigationService.navigator.generateAddress(
                currentPath,
              )}
            </span>
            was not found in the application
          </div>
        </div>
      </div>
    </LegendMarketplacePage>
  );
});

const useProtectedPage = (PageComponent: React.FC): React.FC =>
  withAuthenticationRequired(PageComponent, {
    OnRedirecting: () => (
      <CubesLoadingIndicator isLoading={true}>
        <CubesLoadingIndicatorIcon />
      </CubesLoadingIndicator>
    ),
    signinRedirectArgs: {
      state: `${window.location.pathname}${window.location.search}${window.location.hash}`,
    },
  });

export const LegendMarketplaceWebApplicationRouter = observer(() => {
  const marketplaceBaseStore = useLegendMarketplaceBaseStore();
  const applicationStore = useLegendMarketplaceApplicationStore();

  useEffect(() => {
    if (marketplaceBaseStore.initState.isInInitialState) {
      flowResult(marketplaceBaseStore.initialize()).catch(
        applicationStore.alertUnhandledError,
      );
    }
  }, [applicationStore.alertUnhandledError, marketplaceBaseStore]);

  const ProtectedYourOrders = withAuthenticationRequired(
    LegendMarketplaceYourOrders,
    {
      OnRedirecting: () => (
        <CubesLoadingIndicator isLoading={true}>
          <CubesLoadingIndicatorIcon />
        </CubesLoadingIndicator>
      ),
      signinRedirectArgs: {
        state: `${window.location.pathname}${window.location.search}`,
      },
    },
  );

  // Loading fallback for lazy-loaded components
  const LazyLoadingFallback = (
    <CubesLoadingIndicator isLoading={true}>
      <CubesLoadingIndicatorIcon />
    </CubesLoadingIndicator>
  );

  return (
    <div className="app">
      {marketplaceBaseStore.initState.hasCompleted && (
        <Routes>
          <Route
            element={
              <>
                <MarketplaceLakehouseHeader />
                <CartToast />
                <FeedbackWidget />
                <Suspense fallback={LazyLoadingFallback}>
                  <Outlet />
                </Suspense>
              </>
            }
          >
            <Route
              path={LEGEND_MARKETPLACE_ROUTE_PATTERN.OAUTH_CALLBACK}
              element={<MarketplaceLakehouseOAuthCallback />}
            />

            {/* Marketplace Routes */}
            <Route
              path={
                LEGEND_MARKETPLACE_ROUTE_PATTERN.DATA_PRODUCT_SEARCH_RESULTS
              }
              element={React.createElement(
                useProtectedPage(LegendMarketplaceSearchResults),
              )}
            />
            <Route
              path={LEGEND_MARKETPLACE_ROUTE_PATTERN.FIELD_SEARCH_RESULTS}
              element={React.createElement(
                useProtectedPage(LegendMarketplaceFieldSearchResults),
              )}
            />
            <Route
              path={LEGEND_MARKETPLACE_ROUTE_PATTERN.DATA_APIS}
              element={<LegendMarketplaceDataAPIs />}
            />
            <Route
              path={LEGEND_MARKETPLACE_ROUTE_PATTERN.AGENTS}
              element={<LegendMarketplaceAgents />}
            />
            <Route
              path={LEGEND_MARKETPLACE_ROUTE_PATTERN.INVENTORY}
              element={<LegendMarketplaceInventory />}
            />
            <Route
              path={LEGEND_MARKETPLACE_ROUTE_PATTERN.TERMINAL_ADD_ONS}
              element={<LegendMarketplaceVendorData />}
            />
            <Route
              path={LEGEND_MARKETPLACE_ROUTE_PATTERN.DATA_PRODUCT}
              element={React.createElement(
                useProtectedPage(LakehouseDataProduct),
              )}
            />
            <Route
              path={LEGEND_MARKETPLACE_ROUTE_PATTERN.TERMINAL_PRODUCT}
              element={React.createElement(useProtectedPage(TerminalProduct))}
            />
            <Route
              path={LEGEND_MARKETPLACE_ROUTE_PATTERN.SDLC_DATA_PRODUCT}
              element={<LakehouseSDLCDataProduct />}
            />
            <Route
              path={LEGEND_MARKETPLACE_ROUTE_PATTERN.LEGACY_DATA_PRODUCT}
              element={<LegacyDataProduct />}
            />
            <Route
              path={
                LEGEND_MARKETPLACE_ROUTE_PATTERN.LAKEHOUSE_ENTITLEMENTS_WORKFLOW_DATA_ACCESS_REQUEST
              }
              element={React.createElement(
                useProtectedPage(WorkflowDataAccessRequestTask),
              )}
            />
            <Route
              path={
                LEGEND_MARKETPLACE_ROUTE_PATTERN.LAKEHOUSE_ENTITLEMENTS_PERMIT_DATA_ACCESS_REQUEST
              }
              element={React.createElement(
                useProtectedPage(PermitDataAccessRequestTask),
              )}
            />
            <Route
              path={
                LEGEND_MARKETPLACE_ROUTE_PATTERN.LAKEHOUSE_ENTITLEMENTS_CONTRACT_TASK
              }
              element={React.createElement(
                useProtectedPage(LakehouseDataContractTask),
              )}
            />
            <Route
              path={LEGEND_MARKETPLACE_ROUTE_PATTERN.LAKEHOUSE_ENTITLEMENTS}
              element={React.createElement(
                useProtectedPage(LakehouseEntitlements),
              )}
            />
            <Route
              path={LEGEND_MARKETPLACE_ROUTE_PATTERN.HOME_PAGE}
              element={React.createElement(
                useProtectedPage(MarketplaceLakehouseHome),
              )}
            />
            <Route
              path={LEGEND_MARKETPLACE_ROUTE_PATTERN.LAKEHOUSE_ADMIN}
              element={React.createElement(useProtectedPage(LakehouseAdmin))}
            />

            {/* Reroute pages */}
            <Route
              path={LEGEND_MARKETPLACE_ROUTE_PATTERN.DEPRECATED_LAKEHOUSE}
              element={
                <Navigate
                  to={LEGEND_MARKETPLACE_ROUTE_PATTERN.HOME_PAGE}
                  replace={true}
                />
              }
            />
            <Route
              path={
                LEGEND_MARKETPLACE_ROUTE_PATTERN.DEPRECATED_LAKEHOUSE_SEARCH_RESULTS
              }
              element={
                <Navigate
                  to={
                    LEGEND_MARKETPLACE_ROUTE_PATTERN.DATA_PRODUCT_SEARCH_RESULTS
                  }
                  replace={true}
                />
              }
            />
            <Route
              path={
                LEGEND_MARKETPLACE_ROUTE_PATTERN.DEPRECATED_LAKEHOUSE_PRODUCT
              }
              element={
                <Navigate
                  to={LEGEND_MARKETPLACE_ROUTE_PATTERN.DATA_PRODUCT}
                  replace={true}
                />
              }
            />
            <Route
              path={
                LEGEND_MARKETPLACE_ROUTE_PATTERN.DEPRECATED_LAKEHOUSE_SDLC_PRODUCT
              }
              element={
                <Navigate
                  to={LEGEND_MARKETPLACE_ROUTE_PATTERN.SDLC_DATA_PRODUCT}
                  replace={true}
                />
              }
            />

            {/* Plugin additional pages */}
            {/* We filter out any pages with paths that exist above to avoid overwriting main pages */}
            {applicationStore.pluginManager
              .getApplicationPlugins()
              .flatMap(
                (plugin) =>
                  plugin.getAdditionalMarketplacePageConfigs?.() ?? [],
              )
              .filter(
                (pageConfig) =>
                  !(pageConfig.path in LEGEND_MARKETPLACE_ROUTE_PATTERN),
              )
              .map((pageConfig) => (
                <Route
                  key={pageConfig.path}
                  path={pageConfig.path}
                  element={React.createElement(
                    pageConfig.protected
                      ? useProtectedPage(pageConfig.component)
                      : pageConfig.component,
                  )}
                />
              ))}
            <Route
              path={LEGEND_MARKETPLACE_ROUTE_PATTERN.ORDERS}
              element={<ProtectedYourOrders />}
            />
            <Route
              path={LEGEND_MARKETPLACE_ROUTE_PATTERN.SUBSCRIPTIONS}
              element={<LegendMarketplaceSubscriptions />}
            />
            <Route path="*" element={<NotFoundPage />} />
          </Route>
        </Routes>
      )}
    </div>
  );
});

export const LegendMarketplaceWebApplication = observer(
  (props: {
    baseUrl: string;
    oidcConfig?: LegendMarketplaceOidcConfig | undefined;
  }) => {
    const { baseUrl, oidcConfig } = props;

    const onSigninCallback = (_user: User | undefined) => {
      window.location.href = (_user?.state as string | undefined) ?? '/';
    };

    const mergedOIDCConfig: AuthProviderProps | undefined = oidcConfig
      ? {
          ...oidcConfig.authProviderProps,
          redirect_uri: `${window.location.origin}${oidcConfig.redirectPath}`,
          silent_redirect_uri: `${window.location.origin}${oidcConfig.silentRedirectPath}`,
          onSigninCallback,
        }
      : undefined;

    return (
      <AuthProvider {...mergedOIDCConfig}>
        <LegendTokenSync>
          <BrowserEnvironmentProvider baseUrl={baseUrl}>
            <LegendMarketplaceFrameworkProvider>
              <LegendMarketplaceWebApplicationRouter />
            </LegendMarketplaceFrameworkProvider>
          </BrowserEnvironmentProvider>
        </LegendTokenSync>
      </AuthProvider>
    );
  },
);
