import * as Sentry from '@sentry/react'
import { Alert, Flex, Typography } from '@tidbcloud/uikit'
import { IconAlertCircle } from '@tidbcloud/uikit/icons'
import { observer } from 'mobx-react'
import { Suspense, useEffect, useMemo, useState } from 'react'
import { Route, Redirect, Switch, useHistory, useLocation } from 'react-router-dom'

import 'driver.js/dist/driver.css'
import './utils/driver/driver.theme.css'

import { log } from 'common/log'
import { LOG_ERROR_SCENE } from 'common/log/logger'
import config from 'config'
import PageLoader from 'dbaas/components/Placeholder'
import { AccessProvider } from 'dbaas/contexts/access-control'
import { AppContainer } from 'dbaas/layouts/v5'
import * as Vercel from 'dbaas/screens/Vercel'
import useStores from 'dbaas/stores/useStores'

import { ErrorPanel } from './components/Error'
import Link from './components/Link'
import { useSyncLastVisitLocation } from './features/Organization/mode'
import { CloudProviderConst } from './features/Project'
import { layoutFreeRoutes } from './routes/protected/layout-free'
import { landingRoute } from './screens/Landing/lazy'
import {
  checkDedicatedOnboardingRequired,
  dedicatedOnboardingRoute,
  isSignupFromDedicated,
  markDedicatedOnboardingDone
} from './screens/Onboarding/Dedicated'
import {
  checkServerlessOnboardingRequired,
  markServerlessOnboardingDone,
  serverlessOnboardingRoute
} from './screens/Onboarding/Serverless'
import { surveyRoute } from './screens/Onboarding/SurveyV2'
import { markSurveyDone, surveyRequired } from './screens/Onboarding/SurveyV2/helpers'
import { isToChat2Query } from './screens/User/Common/Chat2Query'
import { CentralAuth0AuditLogReqOperation, updateAuth0AuditLog, useListLoginUserRoles } from './services'
import { getUserLoginExtInfo } from './services/account'
import auth from './services/auth'
import { ErrorMessage, pickErrorResponse } from './services/errorCodes'
import { ExpFeatHashmap, getAuthInfo, getChannelLanding, getLastPage, setChannelLanding } from './stores/localStorage'
import { ram } from './stores/ram'
import {
  buildLakeConnectUrlFromSearch,
  getLakeConnectIntentFromSearch,
  getLakeConnectTrackingProperties
} from './utils/lake-connect'
import { getThirdPartyParams } from './utils/marketplace'
import { getSafeReturnTo } from './utils/returnTo'
import { eventTracking, identifyUser, MixpanelEvents, reportReo, usePageViewTracking } from './utils/tracking'
import { enableBranchTesting } from './utils/tracking/enableBranchTesting'
import { matchRedirect, useLastPage } from './utils/tracking/useLastPage'
import { ORIENTATION, useOrientation } from './utils/useOrientation'
import useQuery from './utils/useQuery'

const UNKNOWN_ERROR = 'UNKNOWN_ERROR'

const Console = observer(() => {
  const history = useHistory()
  const location = useLocation()
  const query = useQuery()

  const {
    actions,
    store: { userProfile, tenantData, activeProjectId, currentCluster },
    actions: { toggleSupportVisible }
  } = useStores()
  const { fetchFeatureList } = actions

  const [fetched, setFetched] = useState(false)
  const [error, setError] = useState('')
  const { orientation } = useOrientation()

  const { syncLastVisitLocation } = useSyncLastVisitLocation()

  const checkRedirect = (userId: string) => {
    const lastPage = getLastPage(userId)

    // redirect to link marketplace if third_party_account_provider exists
    const { thirdPartyParams: marketplaceParams, fromMarketplace } = getThirdPartyParams(query)

    const needOpenSupportPlan = location.search.includes('openSupportPlan=true')

    if (fromMarketplace) {
      history.push(`/link-marketplace?${marketplaceParams}`)
    }
    // only check the last page during the root router
    else if (location.pathname === '/' && lastPage && lastPage.pathname && matchRedirect(lastPage.pathname)) {
      history.replace(lastPage)
    }

    if (needOpenSupportPlan) {
      toggleSupportVisible()
    }
  }

  const reportSignin = (user: { email: string; firstname: string; lastname: string }) => {
    if (ram.get('reportReo') && config.ENABLE_REO) {
      setTimeout(() => {
        reportReo({
          username: user.email,
          type: 'email',
          firstname: user.firstname,
          lastname: user.lastname
        })
        ram.clear('reportReo')
      }, 6 * 1000)
    }
  }

  // access control will block the hero component
  // so fetch the data ASAP
  useListLoginUserRoles(undefined, {
    query: {
      refetchInterval: false,
      refetchOnWindowFocus: false
    },
    request: {
      isHandleErr: true,
      _ignoreStatus: [401]
    }
  })

  usePageViewTracking(location.pathname)

  const { enableLastPage } = useLastPage(userProfile.id)

  useEffect(() => {
    if (tenantData.id && activeProjectId) {
      actions.fetchWhiteList()
    }
  }, [currentCluster?.id])

  useEffect(() => {
    async function fetcher() {
      try {
        const lakeConnectIntent = getLakeConnectIntentFromSearch(query)
        const lakeConnectUrl = buildLakeConnectUrlFromSearch(query)

        let currentOrgId = ''
        const { currentOrgId: orgId, lastVisit } = await syncLastVisitLocation()

        if (orgId) {
          currentOrgId = orgId
        }

        if (lakeConnectIntent && lakeConnectUrl) {
          await getUserLoginExtInfo({
            _ignoreStatus: [401],
            isHandleErr: true
          })
          eventTracking(MixpanelEvents.TidbCloudLake.View.EnterClicked, {
            entry: 'sso_authenticated',
            ...getLakeConnectTrackingProperties(lakeConnectIntent)
          })
          window.location.replace(lakeConnectUrl)
          return
        }

        // join alibaba cloud program
        if (query.get('provider_source') === CloudProviderConst.ALICLOUD) {
          const whiteList = await actions.fetchWhiteList()
          if (!whiteList.exp_names?.includes(ExpFeatHashmap.EnableAlibabaCloudOrgs)) {
            history.replace('/program/join')
            setFetched(true)
            return
          }
        }

        // A valid returnTo means the user came from OAuth and can skip the survey.
        const returnTo = getSafeReturnTo(query.get('returnTo'))
        if (returnTo) {
          history.replace(returnTo)
          setFetched(true)
          return
        }

        /**
         * For the first login of a newly self-registered user
         */
        if (surveyRequired() || checkServerlessOnboardingRequired() || checkDedicatedOnboardingRequired()) {
          /**
           * If an un-registered user log in by google/github/ms,
           * invoking `getUserLoginExtInfo` will return 401,
           * we need `isHandleErr` option to handle error manually
           * instead of handling by the global axios interceptors.
           * Otherwise, the page will be redirected to the login page.
           * If `isHandleErr` is true and we get 401, the page
           * will be redirected to `signup-sso`.
           */
          const userExtInfo = await getUserLoginExtInfo({
            _ignoreStatus: [401],
            isHandleErr: true
          })

          actions.updateLocalUserProfile({
            first_name: userExtInfo.first_name || '',
            last_name: userExtInfo.last_name || ''
          })
          reportSignin({
            email: getAuthInfo().email || '',
            firstname: userExtInfo.first_name || '',
            lastname: userExtInfo.last_name || ''
          })

          const isSurveyRequired = userExtInfo.is_show_plg_on_boarding_survey_page
          const isNewlyRegistered = !!(userExtInfo.is_signup_user && userExtInfo.is_first_login)
          const isServerlesssOnboarding =
            isNewlyRegistered && !userExtInfo.signup_source && checkServerlessOnboardingRequired()
          const isDedicatedOnboarding =
            isNewlyRegistered && isSignupFromDedicated(userExtInfo.signup_source) && checkDedicatedOnboardingRequired()

          if (isSurveyRequired && isNewlyRegistered) {
            setFetched(true)

            const params = [`trigger=${isNewlyRegistered ? 'signup' : 'login'}`]

            if (userExtInfo.signup_source) {
              params.push(`signupSource=${userExtInfo.signup_source}`)
            }

            if (isServerlesssOnboarding) {
              params.push(`next=${encodeURIComponent('/')}`)
            } else if (isDedicatedOnboarding) {
              params.push(`next=${encodeURIComponent(dedicatedOnboardingRoute.path)}`)
            }

            return history.replace(`${surveyRoute.path}?${params.join('&')}`)
          } else {
            markSurveyDone()
          }

          if (isServerlesssOnboarding) {
            /**
             * FIXME: Now set `fetched` to true for correct routes render
             */
            setFetched(true)

            return history.replace(serverlessOnboardingRoute.path)
          } else {
            markServerlessOnboardingDone()
          }

          if (isDedicatedOnboarding) {
            setFetched(true)

            return history.replace(dedicatedOnboardingRoute.path)
          } else {
            markDedicatedOnboardingDone()
          }
        }

        // for marketing
        if (isToChat2Query(query)) {
          const [user, tenant] = await actions.fetchUserProfileV2()

          Sentry.setUser({
            id: user.user_id,
            email: user.email
          })
          // logging to mixpanel
          identifyUser(
            { ...user, id: user.user_id },
            { third_party_account_provider: tenant.third_party_account_code || '' }
          )
          reportSignin({ email: user.email, firstname: user.first_name, lastname: user.last_name })
          fetchFeatureList(tenant?.id)
          if (user.is_new_user && !getChannelLanding(tenant.email) && location.pathname !== landingRoute.path) {
            setChannelLanding(tenant.email)
            history.push(landingRoute.path)
          } else {
            checkRedirect(user.user_id)
            enableLastPage()
          }
        } else {
          fetchFeatureList(lastVisit?.org_id)

          currentOrgId = lastVisit?.org_id

          actions.fetchUserProfileV2().then((data) => {
            const [user, tenant] = data
            Sentry.setUser({
              id: user.user_id,
              email: user.email
            })

            // logging to mixpanel
            identifyUser(
              { ...user, id: user.user_id },
              { third_party_account_provider: tenant.third_party_account_provider || '' }
            )
            reportSignin({ email: user.email, firstname: user.first_name, lastname: user.last_name })
            checkRedirect(user.user_id)
            enableLastPage()
            // retrieve billing info
            actions.fetchTenantPlanInfo()
          })
        }

        actions.fetchWhiteList().then((data) => {
          enableBranchTesting(data.exp_names)
          if (data.exp_names?.includes(ExpFeatHashmap.CookieEnable)) {
            localStorage.removeItem('id_token')
            localStorage.removeItem('access_token')
          }
        })

        // for user that from auth callback other than refresh the page need to report
        if (ram.get('reportLogin')) {
          eventTracking('Login')
          updateAuth0AuditLog(
            {
              operation: CentralAuth0AuditLogReqOperation.SIGNIN,
              is_success: true,
              failure_info: '',
              org_id: currentOrgId || ''
            },
            { isHandleErr: true }
          )
          ram.clear('reportLogin')
        }

        setFetched(true)
      } catch (e) {
        const { status, message } = pickErrorResponse(e)
        const fixedMsg = message?.toLocaleLowerCase() || ''

        if (status === 401 || status === 403) {
          switch (fixedMsg) {
            case ErrorMessage.PENDING_SIGNUP_USER:
              // signup user is pending
              // the user has self-registered and has not activated the account in the email
              const email = localStorage.getItem('sign_in_email')
              history.replace(`/signup?status=verifying&email=${encodeURIComponent(email || '')}`)
              break
            case ErrorMessage.PENDING_SIGNUP_SSO_USER:
              history.replace('/forgot_password?type=pending')
              break
            case ErrorMessage.UNREGISTERED_SSO_USER:
              // only sso user can go to signup-sso
              history.replace(`/signup-sso${window.location.search}`)
              break
            case ErrorMessage.INVALID_TOKEN:
            case ErrorMessage.UNAUTHORIZED:
            case ErrorMessage.UNAUTHORIZED_SESSION_EXPIRED:
              auth.logout()
              break
            case ErrorMessage.PENDING_USER:
            case ErrorMessage.UNREGISTERED_USER:
            case ErrorMessage.INACTIVE_USER:
            case ErrorMessage.USER_NOT_IN_ORG:
            case ErrorMessage.ENTERPRISE_USER_PROVISIONING_DISABLED:
              // invited pending user: the user did not accept the invitation in the email
              // unregistered user: the user has not registered in the system
              // inactive user: the user has been deleted
              setError(fixedMsg)
              break
            default:
              // other auth error cases
              if (fixedMsg.toLowerCase().includes('unauthorized')) {
                auth.logout()
                return
              }
              setError(UNKNOWN_ERROR)
          }
        } else {
          log.error('Get Orgs Me Fail', e as Error, {
            tags: {
              scene: LOG_ERROR_SCENE.CoreJourney
            }
          })
          setError(fixedMsg)
        }
      }
    }

    fetcher()
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  const mobileAlarm = useMemo(() => {
    const isMobile = window.innerWidth < 768
    const isVertical = orientation === ORIENTATION.PORTRAIT
    return isMobile && isVertical
  }, [orientation])

  return fetched ? (
    <AccessProvider>
      {mobileAlarm && (
        <Alert color="yellow" icon={<IconAlertCircle />} classNames={{ icon: 'wd-mr-0' }}>
          Please rotate your mobile device for horizontal use.
        </Alert>
      )}
      <Suspense fallback={<PageLoader />}>
        <Switch>
          <Route path={surveyRoute.path} component={surveyRoute.component} exact />
          <Route path={serverlessOnboardingRoute.path} component={serverlessOnboardingRoute.component} exact />
          <Route path={dedicatedOnboardingRoute.path} component={dedicatedOnboardingRoute.component} exact />
          <Route path={landingRoute.path} component={landingRoute.component} exact />
          {/* layout free */}
          {layoutFreeRoutes.map((route) => (
            <Redirect key={route.path} from={`/console/${route.path}`} to={route.path} exact />
          ))}

          {layoutFreeRoutes.map((route) => (
            <Route key={route.path} path={route.path} component={route.component} exact />
          ))}

          {/* Ecosystem/Vercel */}
          <Route path="/console/ecosystem/vercel/link" component={Vercel.AddIntegration} exact />
          <Route path="/console/ecosystem/vercel/configuration" component={Vercel.Configuration} exact />
          <Route path="/console/ecosystem/vercel/redirect" component={Vercel.Redirect} exact />
          {/* App Layout */}
          <Route path="/" component={AppContainer.component} />
        </Switch>
      </Suspense>
    </AccessProvider>
  ) : error === ErrorMessage.UNREGISTERED_USER || error === ErrorMessage.INACTIVE_USER ? (
    <ErrorPanel withRetryLogin>
      <Flex justify="center">
        {error === ErrorMessage.INACTIVE_USER ? (
          <Typography variant="headline-md" ta="center">
            Your account has been deactivated. Please use another account or contact{' '}
            <Link href="mailto:tidbcloud-support@pingcap.com" eventName="Tidbcloud Email Support Link Clicked" /> for
            help.
          </Typography>
        ) : (
          <Typography variant="headline-md" ta="center">
            Your login email does not exist in TiDB Cloud.
          </Typography>
        )}
      </Flex>
    </ErrorPanel>
  ) : error === ErrorMessage.PENDING_USER ? (
    <ErrorPanel>
      <Flex direction="column" justify="center">
        <Typography component="p" variant="body-lg" ta="center">
          You have been invited to join TiDB Cloud, please accept the invitation in your email first.
        </Typography>
        <Typography component="p" variant="body-lg" ta="center">
          Please{' '}
          <Link
            href="/"
            eventName="Sign In Link Clicked"
            onClick={() => {
              auth.logout()
            }}
          >
            sign in
          </Link>{' '}
          after accepting your email invitation.{' '}
        </Typography>
        <Typography component="p" variant="body-lg" ta="center">
          If you did not receive an invitation email, please contact your organization owner to resend the invitation.
        </Typography>
      </Flex>
    </ErrorPanel>
  ) : error === ErrorMessage.USER_NOT_IN_ORG ? (
    <ErrorPanel withRetryLogin withSupport>
      <Flex direction="column" justify="center">
        <Typography component="p" variant="body-lg" ta="center">
          You are not currently associated with any organization in TiDB Cloud.
        </Typography>
      </Flex>
    </ErrorPanel>
  ) : error === ErrorMessage.ENTERPRISE_USER_PROVISIONING_DISABLED ? (
    <ErrorPanel withSupport withRetryLogin>
      <Flex direction="column" justify="center">
        <Typography component="p" variant="body-lg" ta="center">
          Your organization has disabled user provisioning. Please contact your organization owner to invite you.
        </Typography>
      </Flex>
    </ErrorPanel>
  ) : error ? (
    <ErrorPanel withSupport withRetryLogin />
  ) : (
    <PageLoader />
  )
})

export default Console
