import { Alert, Card, Divider, Flex, Group, Typography, Button, Image, Box } from '@tidbcloud/uikit'
import { Form, FormTextInput } from '@tidbcloud/uikit/biz'
import { IconInfoCircle } from '@tidbcloud/uikit/icons'
import { lowerCase, startCase } from 'lodash-es'
import React, { Suspense, useMemo } from 'react'
import { Link, useParams } from 'react-router-dom'

import { Logo } from 'dbaas/components'
import PageLoader from 'dbaas/components/Placeholder'
import { AUTH_METHOD_TYPE } from 'dbaas/features/Organization'
import { NameFields } from 'dbaas/screens/User/Common/FormFields'
import { createAuth0User } from 'dbaas/services'
import { useListOrgActiveAuthMethods } from 'dbaas/services/account'
import auth, { AUTH_CALLBACK_URI, ConnectionType, databaseConnection } from 'dbaas/services/auth'
import { getErrorMessage, is404Error } from 'dbaas/services/errorCodes'

import { AcceptServices, MSPAgreement } from '../Common/AcceptServices'
import { BackgroundMask } from '../Common/Background'
import { useMSPCustomer } from '../Common/MSP'
import { SSOButton } from '../Common/SSO'
import { InviteUserInfo } from '../InviteeAccepting/Enterprise'

const ErrorBox: React.FC<React.PropsWithChildren<{ title?: string; message?: string }>> = ({ title, message }) => {
  return (
    <Flex align="center" direction="column" sx={() => ({ zIndex: 1 })}>
      <Flex justify="center">
        {!!title && (
          <Typography fw={900} fz={96} lh="96px" c="carbon.9">
            {title}
          </Typography>
        )}
        <Typography ml={24} fw={900} fz={96} lh="96px" c="carbon.7">
          Error
        </Typography>
      </Flex>
      <Typography ta="center" maw={390} mt={16} variant="body-lg">
        {message}
      </Typography>
      <Button mt={24} variant="default" onClick={() => auth.logout()} data-mp-event="mp-backtosignin-1691461013725">
        Back to sign in
      </Button>
    </Flex>
  )
}

interface UserInfo {
  email: string
  password: string
  first_name?: string
  last_name?: string
}

const AUTH_METHOD_CONNECTION = {
  [AUTH_METHOD_TYPE.OIDC]: 'oidc',
  [AUTH_METHOD_TYPE.SAML]: 'saml'
}

export const EnterpiseSignIn: React.FC<
  React.PropsWithChildren<{
    type?: 'signin' | 'invite'
    beforeAction?: (connection: string, extra?: Pick<InviteUserInfo, 'first_name' | 'last_name'>) => void
  }>
> = ({ type = 'signin', beforeAction }) => {
  const params = useParams<{ company: string }>()
  const isInvite = type === 'invite'

  const search = window.location.search
  const urlParams = useMemo(() => new URLSearchParams(search), [search])
  const orgId = urlParams.get('org_id') || '' //invite user from a exist tenant
  const companyName = urlParams.get('company-name') || '' //invite user from a exist tenant
  const existed = urlParams.get('existed') === 'true'
  const shouldRegister = isInvite && !existed
  const msp = urlParams.get('msp') || ''

  const { data, isError, error, isLoading } = useListOrgActiveAuthMethods(
    {
      org_id: orgId,
      company_name: params.company || companyName || undefined
    },
    {
      query: {
        retry: false,
        refetchInterval: false,
        refetchOnWindowFocus: false
      }
    }
  )
  const { logo_url, disclaimers } = useMSPCustomer(msp)

  const orgName = data?.org_name || 'Org'

  const getRedirectParams = () => {
    let url = `?usage=${type === 'invite' ? 'enterprise-invite' : 'enterprise-sigin'}&company=${
      params.company || companyName
    }`
    const keys = ['org_id']
    keys.forEach((key) => {
      const val = urlParams.get(key)
      if (val) {
        url += `&${key}=${val}`
      }
    })
    return url
  }

  const onClick = (vendor: 'GOOGLE' | 'GITHUB' | 'MICROSOFT' | 'OIDC' | 'SAML') => () => {
    beforeAction?.(vendor)
    auth.authorizeWithVendor(vendor, getRedirectParams())
  }

  const onLogin = async (data: UserInfo) => {
    return new Promise((_resolve, reject) => {
      return auth.auth0.login(
        {
          realm: databaseConnection,
          email: data.email,
          password: data.password,
          redirectUri: `${AUTH_CALLBACK_URI}${getRedirectParams()}`
        },
        function (err) {
          const errMsg = err?.description || ''
          reject(new Error(errMsg))
        }
      )
    })
  }

  const registerUser = async (data: UserInfo) => {
    await createAuth0User(orgId, {
      password: data.password,
      token: urlParams.get('token') || '',
      org_id: orgId
    })
    beforeAction?.(databaseConnection, {
      first_name: data.first_name || '',
      last_name: data.last_name || ''
    })
    // auto login
    await onLogin(data)
  }

  const is404 = is404Error(error)

  return (
    <Suspense fallback={<PageLoader />}>
      <Flex h={'100vh'} direction="column" align="center" justify="center" bg="white" pos="relative">
        <BackgroundMask />
        <Box sx={{ position: 'absolute', left: 24, top: 24, display: 'flex', alignItems: 'center' }}>
          <Logo withText width={240} height={40}></Logo>
          {!!logo_url && (
            <Image src={logo_url} width="auto" height={40} fit="fill" sx={{ maxWidth: 4 * 40, marginLeft: -48 }} />
          )}
        </Box>
        {isLoading ? null : isError ? (
          <ErrorBox
            title={is404 ? '404' : ''}
            message={
              is404
                ? "We're sorry, but the page you requested could not be found. Please check the URL and try again."
                : getErrorMessage(error)
            }
          />
        ) : (
          <Card p={24} w={460}>
            <Typography variant="headline-md">Sign In</Typography>
            <Typography variant="body-lg">You are going to sign in to {orgName}.</Typography>
            {isInvite && (
              <Alert icon={<IconInfoCircle size={14} />} mt={24}>
                <Typography>
                  You have been invited to join the <b>{orgName}</b>. To accept the invitation, please sign in.
                </Typography>
              </Alert>
            )}
            <Card.Section py={24}>
              <Divider orientation="horizontal" />
            </Card.Section>
            <Group>
              {data?.auth_methods?.map((method) => {
                const connection = ConnectionType[method.name || ''] || method.name || ''
                const connetionType = AUTH_METHOD_CONNECTION[method.type || ''] || connection
                if (method.type === AUTH_METHOD_TYPE.PASSWORD) {
                  return null
                }
                return (
                  <SSOButton key={method.name} connection={connetionType.toLowerCase()} onClick={onClick(connection)}>
                    Continue with {method.display_name ? method.display_name : startCase(lowerCase(connection))}
                  </SSOButton>
                )
              })}
              {data?.auth_methods?.map((method) => method.type).includes(AUTH_METHOD_TYPE.PASSWORD) && (
                <>
                  <Form<UserInfo>
                    w="100%"
                    defaultValues={{
                      email: urlParams.get('email') || '',
                      password: '',
                      first_name: '',
                      last_name: ''
                    }}
                    onSubmit={
                      shouldRegister
                        ? registerUser
                        : async (data) => {
                            beforeAction?.(databaseConnection)
                            await onLogin(data)
                          }
                    }
                    actionsProps={{
                      cancelProps: { display: 'none' },
                      confirmText: isInvite ? 'Join In' : 'Sign In',
                      confirmProps: { w: '100%' }
                    }}
                  >
                    <Divider label="or" labelPosition="center"></Divider>
                    {shouldRegister && <NameFields />}
                    <FormTextInput
                      name="email"
                      disabled={isInvite}
                      placeholder="Email"
                      mt={shouldRegister ? -16 : undefined}
                      rules={{ required: 'Required' }}
                    />
                    <FormTextInput
                      type="password"
                      autoComplete="current-password"
                      name="password"
                      placeholder="Password"
                      rules={{
                        required: 'Required ',
                        minLength: {
                          value: 8,
                          message: 'At least 8 characters in length.'
                        },
                        maxLength: {
                          value: 64,
                          message: 'Maximum 64 characters in length.'
                        },
                        pattern: {
                          value: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{8,64}$/,
                          message:
                            'The password should contain the following types of characters: upper case letters (A-Z), lower case letters (a-z), numbers (such as 0-9).'
                        }
                      }}
                    />
                    {isInvite && (!!disclaimers ? <MSPAgreement agreement={disclaimers} /> : <AcceptServices />)}
                  </Form>
                  {!shouldRegister && (
                    <Typography
                      variant="body-lg"
                      sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%' }}
                    >
                      <Box
                        component={Link}
                        to="/forgot_password"
                        data-mp-event="Forgot Password Link Clicked"
                        data-mp-source={`enterprise-${type}`}
                        mr={32}
                      >
                        Forgot password?
                      </Box>
                      <div>
                        Questions?{' '}
                        <a
                          href="https://en.pingcap.com/contact-us/"
                          target="_blank"
                          rel="noreferrer"
                          data-mp-event="Contact Us Link Clicked"
                          data-mp-source={`enterprise-${type}`}
                        >
                          Contact us
                        </a>
                      </div>
                    </Typography>
                  )}
                </>
              )}
            </Group>
          </Card>
        )}
      </Flex>
    </Suspense>
  )
}

export default EnterpiseSignIn
