import { Box, Button, Card, CardProps, Center, Divider, Flex, Stack, Text, Tooltip, Typography } from '@tidbcloud/uikit'
import { Form, FormSelect } from '@tidbcloud/uikit/biz'
import { IconAlertTriangle, IconCheck, IconCheckCircle, IconInfoCircle, IconXCircle } from '@tidbcloud/uikit/icons'
import { useMemo, useState } from 'react'
import { useForm } from 'react-hook-form'

import { RBACRoles, RBACRoleUINames } from 'dbaas/contexts/access-control'
import { TreeSelectOption } from 'dbaas/screens/OrgSettings/features/Billing/components/TreeSelect'
import { useGetUserOrgsProjects } from 'dbaas/services/account'
import { CentralOrgInfo } from 'dbaas/services/account/interface'
import auth from 'dbaas/services/auth'
import { pickErrorResponse } from 'dbaas/services/errorCodes'
import { approveAuthorizationCode } from 'dbaas/services/oauth2'
import {
  Oauth2InnerAuthorizationCodeCheckRequest,
  Oauth2InnerAuthorizationCodeConfirmationRequest,
  Oauth2InnerAuthorizationCodeResponse
} from 'dbaas/services/oauth2/interface'
import { eventTracking } from 'dbaas/utils/tracking'

import { FormTreeSelect } from '../Components/FormTreeSelect'
import { useIsMdScreen } from '../Components/helpers'
import { PageShell } from '../Components/Shell'

import { compareLevel, getScopes, handle401Error, useCheckAuthorizationCode } from './helpers'

const Tips: React.FC<React.PropsWithChildren<{}>> = ({ children }) => {
  return (
    <Flex bg="carbon.1" p={8} gap={10} mt={16}>
      <Center>
        <IconCheck size={16} c="peacock.7" />
      </Center>
      <Box sx={{ flex: 1 }}>{children}</Box>
    </Flex>
  )
}

const ErrorPlaceholder: React.FC<React.PropsWithChildren<{}>> = ({ children }) => {
  const isMd = useIsMdScreen()
  const cardProps: Omit<CardProps, 'children'> = isMd
    ? {
        w: '100%',
        h: 248,
        shadow: 'none',
        py: 40
      }
    : {
        h: 248,
        py: 24,
        shadow: 'none'
      }
  return (
    <Box w="100%" mt={isMd ? 40 : 0} maw={480}>
      <Card shadow="none" {...cardProps}>
        <Stack align="center">
          <IconXCircle size={40} c="red.7" />
          <Flex direction="column" justify="center">
            {children}
          </Flex>
        </Stack>
      </Card>
    </Box>
  )
}

const CloseWindow = () => {
  return (
    <Button
      variant="default"
      mt={24}
      onClick={() => window.close()}
      sx={(theme) => ({ backgroundColor: theme.colors.carbon[2] })}
    >
      Close
    </Button>
  )
}

const ActionTips: React.FC<React.PropsWithChildren<{ action: 'accept' | 'decline'; name: string }>> = ({
  action,
  name
}) => {
  const isMd = useIsMdScreen()
  const cardProps: Omit<CardProps, 'children'> = isMd
    ? {
        w: '100%',
        mih: 248,
        shadow: 'none',
        py: 40
      }
    : {
        mih: 248,
        py: 24,
        shadow: 'none'
      }

  return (
    <Box px={40} py={24}>
      <Card shadow="none" {...cardProps}>
        <Stack align="center">
          {action === 'decline' ? (
            <>
              <IconAlertTriangle size={40} c="yellow.7" />
              <Typography variant="action-lg" mt={24} c="carbon.8" ta="center">
                You rejected {name} OAuth request.
              </Typography>
            </>
          ) : (
            <>
              <IconCheckCircle size={40} c="green.7" />
              <Typography variant="action-lg" mt={24} ta="center" c="carbon.8">
                You already authorized {name} Insight OAuth request.
              </Typography>
            </>
          )}
          <CloseWindow />
        </Stack>
      </Card>
    </Box>
  )
}

interface ItemProps extends React.ComponentPropsWithoutRef<'div'> {
  value: string
  label: string
  tips?: string
  disabled?: boolean
  leftSection?: React.ReactNode
}

const SelectItem = ({ label, hidden, tips, leftSection: icon, value, ...others }: ItemProps) => {
  if (hidden) {
    return null
  }
  const elem = (
    <Text truncate maw="80%">
      {label}
    </Text>
  )
  return (
    <Box sx={{ width: '100%' }} {...others}>
      {tips ? (
        <Flex align="center" gap={8}>
          {elem}
          <Tooltip withArrow maw={220} multiline label="Your account doesn't have sufficient roles.">
            <IconInfoCircle size={16} />
          </Tooltip>
        </Flex>
      ) : (
        elem
      )}
    </Box>
  )
}

export const Authorize = () => {
  const isMd = useIsMdScreen()
  const [action, setAction] = useState<'accept' | 'decline' | null>(null)
  const params = new URLSearchParams(window.location.search)

  const [orgRole, projectRole] = getScopes(params.get('scope') || '')

  const [error, setError] = useState(!orgRole ? 'Invalid Scope' : '')

  const cardProps: Omit<CardProps, 'children'> = isMd
    ? {
        w: '100%',
        mx: 'auto',
        withBorder: false,
        shadow: 'none',
        p: 0,
        radius: 0
      }
    : {
        w: 650,
        shadow: 'none'
      }

  const form = useForm<Oauth2InnerAuthorizationCodeConfirmationRequest>({
    defaultValues: {
      org_id: '',
      project_ids: undefined
    }
  })

  const { watch } = form

  const selectedOrg = watch('org_id')

  const authCheckPayload: Oauth2InnerAuthorizationCodeCheckRequest = {
    scope: params.get('scope') || '',
    client_id: params.get('client_id') || '',
    redirect_uri: params.get('redirect_uri') || '',
    response_type: params.get('response_type') || '',
    state: params.get('state') || ''
  }

  const { data: authorizationData } = useCheckAuthorizationCode(authCheckPayload, {
    request: {
      isHandleErr: true,
      _warningStatus: [400, 401]
    },
    query: {
      retry: false,
      refetchInterval: false,
      refetchOnWindowFocus: false,
      onError(err: any) {
        if (err?.response?.status === 401) {
          return handle401Error()
        }
        const errResponse = pickErrorResponse(err) as Oauth2InnerAuthorizationCodeResponse
        if (errResponse.need_redirect) {
          window.location.href = errResponse.redirect_to || ''
          return
        }
        setError(errResponse.message || '')
      }
    }
  })

  const { data } = useGetUserOrgsProjects({
    request: {
      isHandleErr: true,
      _warningStatus: [400, 401]
    },
    query: {
      refetchInterval: false,
      retry: false,
      refetchOnWindowFocus: false,
      onSuccess(data) {
        if (data.orgs?.length === 1 && data.projects?.length === 1) {
          const org = data.orgs[0]
          const project = data.projects[0]
          if (compareLevel(org.org_role || '', orgRole, 'org') >= 0) {
            form.setValue('org_id', org.id || '')
          }
          if (project.org_id === org.id || compareLevel(project.project_role || '', projectRole, 'project') >= 0) {
            form.setValue('project_ids', [project.id || ''])
          }
        }
      },
      onError(err: any) {
        if (err?.response?.status === 401) {
          handle401Error()
          return
        }
      }
    }
  })

  const orgs = (data?.orgs || []).map((org) => {
    const disabled = compareLevel(org.org_role || '', orgRole, 'org') < 0
    return {
      label: org.name || '',
      value: org.id || '',
      disabled: disabled,
      tips: disabled ? "Your account doesn't have sufficient roles." : undefined
    }
  })

  const orgsById = useMemo(() => {
    return (data?.orgs || []).reduce<Record<string, CentralOrgInfo>>((prev, next) => {
      prev[next.id || ''] = next
      return prev
    }, {})
  }, [orgs])

  const renderSelectItem = (option: TreeSelectOption) => {
    const Elem = (
      <Text truncate maw="80%">
        {option.label}
      </Text>
    )
    if (option.disabled) {
      return (
        <Flex c="carbon.6" align="center" gap={8} sx={{ flex: 1, maxWidth: '90%' }}>
          {Elem}
          <Tooltip withArrow maw={220} multiline label="Your account doesn't have sufficient roles.">
            <IconInfoCircle size={16} />
          </Tooltip>
        </Flex>
      )
    }
    return Elem
  }

  const projectsByOrg = useMemo(() => {
    if (!projectRole) {
      return {}
    }
    return (data?.projects || []).reduce<Record<string, TreeSelectOption[]>>((prev, next) => {
      const orgId = next.org_id || ''
      const nextOrgRole = orgsById[orgId].org_role
      const editable =
        nextOrgRole === RBACRoles.OrgOwner || compareLevel(next.project_role || '', projectRole, 'project') >= 0
      const option: TreeSelectOption = {
        label: next.name || '',
        value: next.id || '',
        isLeaf: true,
        disabled: !editable
      }
      if (prev[orgId]) {
        prev[orgId].push(option)
      } else {
        prev[orgId] = [option]
      }
      return prev
    }, {})
  }, [data?.projects, orgsById, projectRole])

  const projectOptions = projectsByOrg[selectedOrg || ''] || []

  const onSubmit = async (data: { org_id: string; project_ids: string[] }) => {
    try {
      const res = await approveAuthorizationCode({
        ...authCheckPayload,
        // scope=org only has org_id
        ...(projectRole ? data : { org_id: data.org_id })
      })
      eventTracking('Confirmed Authorize')
      if (res.need_redirect) {
        window.location.href = res.redirect_to || ''
        return
      }
      setAction('accept')
    } catch (err) {
      const errResponse = pickErrorResponse(err) as Oauth2InnerAuthorizationCodeResponse
      if (errResponse.need_redirect) {
        window.location.href = errResponse.redirect_to || ''
        return
      }
      throw err
    }
  }

  const onCancel = () => {
    setAction('decline')
  }

  const applicationName = authorizationData?.application?.name || ''

  if (!auth.isAuthenticated()) {
    handle401Error()
    return null
  }

  return (
    <PageShell withLogo={(!error && isMd) || !isMd}>
      {!!error ? (
        <ErrorPlaceholder>
          <Typography variant="headline-lg">Error: {error}</Typography>
          <Flex justify="center">
            <CloseWindow />
          </Flex>
        </ErrorPlaceholder>
      ) : (
        <Card {...cardProps}>
          <Typography ta="center" variant="title-lg">
            Authorize {applicationName}.
          </Typography>
          <Typography ta="center" variant="body-lg" mt={8}>
            <a href={authorizationData?.application?.home_page_uri} target="_blank" rel="noreferrer">
              {applicationName}
            </a>{' '}
            would like your permission to access <b>{data?.username}</b>'s account.
          </Typography>
          <Divider mt={24} mb={0}></Divider>
          {!action ? (
            <Form
              form={form}
              onSubmit={onSubmit}
              px={40}
              py={24}
              actionsProps={{
                cancelText: 'Decline',
                confirmText: 'Authorize',
                justify: 'space-between',
                gap: 24,
                cancelProps: { mr: 0, w: '100%', bg: 'carbon.2', 'data-mp-event': 'Decline Authorize Button Clicked' },
                confirmProps: { w: '100%', 'data-mp-event': 'Confirm Authorize Button Clicked' }
              }}
              onCancel={onCancel}
            >
              <Card shadow="none" p={16}>
                <FormSelect
                  onChange={(value) => {
                    setTimeout(() => {
                      form.clearErrors('project_ids')
                      const projects = (projectsByOrg[value || ''] || []).filter((t) => !t.disabled).map((t) => t.value)
                      form.setValue('project_ids', projects)
                    })
                  }}
                  label={
                    <Typography variant="title-md" mb={8}>
                      TiDB Cloud Organization
                    </Typography>
                  }
                  placeholder="Select TiDB Cloud Organization"
                  name="org_id"
                  rules={{ required: 'Please select your organization.' }}
                  data={orgs}
                  renderOption={({ option }: { option: ItemProps }) => {
                    return <SelectItem {...option} />
                  }}
                ></FormSelect>
                <Tips>
                  <Typography variant="label-sm">
                    {applicationName} requests{' '}
                    <a
                      href="https://docs.pingcap.com/tidbcloud/manage-user-access#organization-roles"
                      target="_blank"
                      rel="noreferrer"
                    >
                      {RBACRoleUINames[orgRole]} Role
                    </a>{' '}
                    to the selected organization.
                  </Typography>
                </Tips>
              </Card>
              {!!projectRole && (
                <Card shadow="none" p="md">
                  <FormTreeSelect
                    label={
                      <Typography variant="title-md" mb={8}>
                        TiDB Cloud Projects
                      </Typography>
                    }
                    placeholder="Select TiDB Cloud Projects"
                    name="project_ids"
                    options={projectOptions}
                    popoverProps={{ width: 'target' }}
                    rules={{
                      validate: (val) => {
                        if (!val || (Array.isArray(val) && !val.length)) {
                          return 'Please select your projects.'
                        }
                        return true
                      }
                    }}
                    renderSelectItem={renderSelectItem}
                  />
                  <Tips>
                    <Typography variant="label-sm">
                      {applicationName} requests{' '}
                      <a
                        href="https://docs.pingcap.com/tidbcloud/manage-user-access#project-roles"
                        target="_blank"
                        rel="noreferrer"
                      >
                        {RBACRoleUINames[projectRole]} Role
                      </a>{' '}
                      to the selected Projects.
                    </Typography>
                  </Tips>
                </Card>
              )}
            </Form>
          ) : (
            <ActionTips action={action} name={applicationName} />
          )}
        </Card>
      )}
    </PageShell>
  )
}

export default Authorize
