import { ErrorMessage } from '@hookform/error-message'
import { zodResolver } from '@hookform/resolvers/zod'
import { Box, Center, Divider, Flex, Typography, notifier } from '@tidbcloud/uikit'
import { TagsInput, TagsInputProps } from '@tidbcloud/uikit'
import { Form, FormActions, FormErrorMessage, FormSelect } from '@tidbcloud/uikit/biz'
import { IconCheckCircle } from '@tidbcloud/uikit/icons'
import { observer } from 'mobx-react'
import { Controller, RegisterOptions, useForm, useFormContext, useWatch } from 'react-hook-form'
import * as z from 'zod'

import { RBACRoles, useAccessCheck } from 'dbaas/contexts/access-control'
import { PageShell } from 'dbaas/layouts/v5/page'
import { checkEmailAlreadyHasOrgRole, useAssignConsoleUserRole, useEditConsoleUserRole } from 'dbaas/services/account'
import { CentralConsoleUser } from 'dbaas/services/account/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'
import { emailRegEx } from 'dbaas/utils/regex'
import { eventTracking } from 'dbaas/utils/tracking'

import { AdvancedRolesSelect, orgRoleOptions, renderRoleOption } from '../shared/roles'

// Zod schema for CentralConsoleUserRole
const consoleUserRoleSchema = z.object({
  scope_type: z.string().optional(),
  scope_id: z.string({ required_error: 'Scope is required.' }).min(1, 'Scope is required.'),
  rbac_role: z.string({ required_error: 'Role is required.' }).min(1, 'Role is required.')
})

// Validation schema
const createFormSchema = (orgId: string, isUpdate: boolean) => {
  return z.object({
    emails: isUpdate
      ? z.array(z.string()).optional()
      : z
          .array(z.string().min(1, 'Email cannot be empty.'))
          .min(1, 'Please enter at least one email address.')
          .refine(
            (emails) => {
              return emails.every((email) => emailRegEx.test(email.trim()))
            },
            {
              message: 'Please enter the valid email address.'
            }
          )
          .refine(
            (emails) => {
              const seen = new Set<string>()
              for (const email of emails) {
                const normalized = email.trim().toLowerCase()
                if (seen.has(normalized)) {
                  return false
                }
                seen.add(normalized)
              }
              return true
            },
            {
              message: 'Duplicate email addresses.'
            }
          )
          .superRefine(async (emails, ctx) => {
            try {
              if (emails.length === 0) {
                return true
              }
              const resp = await checkEmailAlreadyHasOrgRole(orgId, { emails })
              if (resp.hasRoleEmails?.length) {
                const plural = resp.hasRoleEmails.length > 1
                ctx.addIssue({
                  code: z.ZodIssueCode.custom,
                  message: `${resp.hasRoleEmails.join(', ')} ${plural ? 'have' : 'has'} already been assigned roles in this organization.`
                })
                return false
              }
              return true
            } catch (error) {
              console.error(error)
              return true
            }
          }),
    org_role: z.object({
      scope_type: z.string().optional(),
      scope_id: z.string().optional(),
      rbac_role: z.string({ required_error: 'Organization role is required.' }).min(1, 'Organization role is required.')
    }),
    project_roles: z
      .array(consoleUserRoleSchema)
      .optional()
      .refine(
        (roles) => {
          if (!roles) return true
          const scopeIds = roles.map((r) => r.scope_id)
          return scopeIds.length === new Set(scopeIds).size
        },
        {
          message: 'Duplicate project roles are not allowed.'
        }
      ),
    cluster_roles: z
      .array(consoleUserRoleSchema)
      .optional()
      .refine(
        (roles) => {
          if (!roles) return true
          const scopeIds = roles.map((r) => r.scope_id)
          return scopeIds.length === new Set(scopeIds).size
        },
        {
          message: 'Duplicate instance roles are not allowed.'
        }
      )
  })
}

type FormValues = z.infer<ReturnType<typeof createFormSchema>>

interface InvitationModalProps {
  onClose: () => void
  onFinish?: () => void
  user?: CentralConsoleUser
}

interface FormTagsInputProps extends TagsInputProps {
  name: string
  rules?: RegisterOptions
}

const FormTagsInput = ({ name, rules, onChange, ...props }: FormTagsInputProps) => {
  const { control, formState, getFieldState } = useFormContext()
  const { error } = getFieldState(name, formState)

  return (
    <Controller
      control={control}
      name={name}
      rules={rules}
      render={({ field }) => (
        <TagsInput
          {...field}
          {...props}
          onChange={(value) => {
            field.onChange(value)
            onChange?.(value)
          }}
          error={error ? <ErrorMessage errors={formState.errors} name={name} /> : undefined}
        />
      )}
    />
  )
}

export const UserInvitation = observer(({ onClose, onFinish, user }: InvitationModalProps) => {
  const {
    store: {
      tenantData: { id: orgId }
    }
  } = useStores()

  const isUpdate = !!user
  const accessible = useAccessCheck('OrgUsersV2Action')
  const assignConsoleUserRoleMutation = useAssignConsoleUserRole()

  const editConsoleUserRoleMutation = useEditConsoleUserRole()

  const onSubmit = async (values: FormValues) => {
    if (isUpdate) {
      await editConsoleUserRoleMutation.mutateAsync({
        orgId,
        data: {
          user_id: user?.id ?? '',
          org_role: values.org_role,
          project_roles: values.project_roles,
          cluster_roles: values.cluster_roles
        }
      })
      notifier.success(
        <>
          <IconCheckCircle size={16} c="green.7" mr={4} sx={{ verticalAlign: 'middle' }} />
          Roles for <b>{user?.email}</b> was successfully updated.
        </>
      )
      eventTracking('Edit User Role Completed')
    } else {
      const emails = values.emails ?? []
      await assignConsoleUserRoleMutation.mutateAsync({
        orgId,
        data: {
          emails,
          org_role: values.org_role,
          project_roles: values.project_roles,
          cluster_roles: values.cluster_roles
        }
      })
      eventTracking('Invite User Completed')
      const plural = emails.length > 1
      notifier.success(
        <>
          <IconCheckCircle size={16} c="green.7" mr={4} sx={{ verticalAlign: 'middle' }} />
          <b>{emails.length}</b> user{plural ? 's' : ''} have been invited successfully. Please remind the invitee
          {plural ? 's' : ''} to check their email.
        </>
      )
    }
    onFinish?.()
    onClose()
  }

  const form = useForm<FormValues>({
    mode: 'onChange',
    defaultValues: {
      emails: [],
      org_role: user?.org_role ?? {
        rbac_role: RBACRoles.OrgMember
      },
      project_roles: user?.project_roles ?? [],
      cluster_roles: user?.cluster_roles ?? []
    },
    resolver: zodResolver(createFormSchema(orgId, isUpdate))
  })

  const {
    formState: { isSubmitting }
  } = form

  const error = assignConsoleUserRoleMutation.error || editConsoleUserRoleMutation.error
  const loading = isSubmitting || assignConsoleUserRoleMutation.isLoading || editConsoleUserRoleMutation.isLoading

  const watchedEmails = useWatch({ control: form.control, name: 'emails' })
  const watchedProjectRoles = useWatch({ control: form.control, name: 'project_roles' })
  const watchedClusterRoles = useWatch({ control: form.control, name: 'cluster_roles' })
  const totalPeople = watchedEmails?.length || 0
  const totalRoles = 1 + (watchedProjectRoles?.length || 0) + (watchedClusterRoles?.length || 0)

  return (
    <PageShell
      title={isUpdate ? 'Edit Role' : 'Invite User'}
      headerProps={{
        withBack: true,
        onBackClick: onClose
      }}
    >
      <Center mt={24}>
        <Form<FormValues>
          w={1000}
          maw="100%"
          layout="none"
          form={form}
          onSubmit={onSubmit}
          onCancel={onClose}
          withActions={false}
        >
          <Flex
            direction="column"
            gap={24}
            px={24}
            py={24}
            data-testid="user-role-editor-card"
            sx={(theme) => ({
              borderRadius: theme.defaultRadius,
              border: `1px solid ${theme.colors.carbon[4]}`,
              backgroundColor: theme.colors.carbon[0]
            })}
          >
            <Box>
              {isUpdate ? (
                <Typography variant="title-lg" fw={700} c="carbon.8">
                  {user?.email}
                </Typography>
              ) : (
                <Box>
                  <Flex justify="space-between" align="center" mb={16}>
                    <Typography variant="title-md">Send invites to</Typography>
                    {totalPeople > 0 && (
                      <Typography variant="body-md" c="carbon.7" fw={500}>
                        {totalPeople} people
                      </Typography>
                    )}
                  </Flex>
                  <FormTagsInput name="emails" placeholder="Enter email addresses" clearable />
                </Box>
              )}
            </Box>
            {/* <Divider mx="-xl" /> */}
            <Box>
              <Flex justify="space-between" align="center" mb={16}>
                <Typography variant="title-md">Add Role and scope</Typography>
                <Typography variant="body-md" c="carbon.7" fw={500}>
                  {totalRoles} {totalRoles === 1 ? 'role' : 'roles'}
                </Typography>
              </Flex>
              <Flex direction="column" gap={16}>
                <Box>
                  <Typography variant="label-lg" mb="sm" c="carbon.8" fw={700}>
                    Organization Access
                  </Typography>
                  <FormSelect
                    name="org_role.rbac_role"
                    data={orgRoleOptions}
                    h={40}
                    styles={{ input: { height: 40, minHeight: 40 } }}
                    renderOption={renderRoleOption}
                  />
                </Box>
                <Divider />
                <AdvancedRolesSelect />
              </Flex>
            </Box>
            {error && (
              <FormErrorMessage
                mt={16}
                message={getErrorMessage(error)}
                onDismiss={() => {
                  assignConsoleUserRoleMutation.reset()
                  editConsoleUserRoleMutation.reset()
                }}
              />
            )}
            <Divider mx="-xl" />
            <FormActions
              mt={0}
              mb={-8}
              onCancel={onClose}
              confirmText={isUpdate ? 'Save' : 'Invite'}
              confirmProps={{
                loading,
                disabled: !accessible,
                'data-mp-event': isUpdate ? 'Edit User Role Completed' : 'Invite User Completed'
              }}
            />
          </Flex>
        </Form>
      </Center>
    </PageShell>
  )
})
