import {
  Box,
  Divider,
  MenuItem,
  Select,
  SelectChangeEvent,
  Stack,
  TextField,
  Typography,
} from '@mui/material'
import { Controller, SubmitHandler, useForm } from 'react-hook-form'
import React, {
  forwardRef,
  Ref,
  useContext,
  useEffect,
  useImperativeHandle,
  useState,
} from 'react'
import { Trans, useTranslation } from 'react-i18next'
import {
  CountryCallingCode,
  CountryCode,
  getCountries,
  getCountryCallingCode,
} from 'libphonenumber-js'
import { getAlpha2Codes, getName } from 'i18n-iso-countries'
import { DatePicker } from '@mui/lab'
import { subYears } from 'date-fns/fp'
import {
  isValidFnr,
  isValidMobilePhoneNumber,
  extractGenderOrBlank,
  extractBirthdateFromNationalId,
} from 'utils'
import { GuestInviteInformation } from '../guestDataForm'
import { GuestRegisterData } from '../enteredGuestData'
import { GuestRegisterCallableMethods } from '../guestRegisterCallableMethods'
import AuthenticationMethod from '../authenticationMethod'
import { FeatureContext } from '../../../../contexts'
import { FormSetup } from './formSetup'

interface GuestRegisterProperties {
  nextHandler(registerData: GuestRegisterData): void

  initialGuestData: GuestInviteInformation
  registerData: GuestRegisterData | null
}

/**
 * This component is the form where the guest enters missing information about himself and
 * where he can see the data the sponsor has entered and the role. The page may also
 * be populated with data from a third-party like Feide if the guest logged in using that.
 */
const GuestRegisterStep = forwardRef(
  (props: GuestRegisterProperties, ref: Ref<GuestRegisterCallableMethods>) => {
    const { i18n, t } = useTranslation(['common'])
    const { nextHandler, initialGuestData, registerData } = props
    const { showGenderFieldForGuest } = useContext(FeatureContext)

    // For select-components it seems to be easier to tie them to a state
    // and then handle the updating of the form using this, than to tie the
    // components directly to the form-field using useForm
    const [countryCode, setCountryCode] = useState<
      CountryCallingCode | undefined
    >(undefined)
    const [passportNationality, setPassportNationality] = useState<
      string | undefined
    >(undefined)

    // Set suggestion for gender field is a gender is not already given in the input
    const [gender, setGender] = useState<string>(
      initialGuestData.gender ?? extractGenderOrBlank(initialGuestData.fnr)
    )
    const [idErrorState, setIdErrorState] = useState<string>('')
    const [phoneErrorState, setPhoneErrorState] = useState<string>('')
    const { displayContactAtUnitGuestInput } = useContext(FeatureContext)

    console.log('register step registerData', registerData)

    const {
      register,
      handleSubmit,
      setValue,
      setError,
      clearErrors,
      control,
      trigger,
      formState: { errors },
    } = useForm<GuestRegisterData>({
      defaultValues: registerData ?? {},
    })

    // If there is no already a date of birth set, add a suggestion for
    // this value based on the national ID, if it is set
    if (
      (!registerData || !registerData.dateOfBirth) &&
      !initialGuestData.date_of_birth &&
      initialGuestData.fnr
    ) {
      const dateOfBirth = extractBirthdateFromNationalId(initialGuestData.fnr)
      if (dateOfBirth) {
        setValue('dateOfBirth', dateOfBirth)
      }
    }

    const submit: SubmitHandler<GuestRegisterData> = async (data) => {
      console.log('submit data is', data)
      const result = await trigger()
      console.log('trigger result is', result)
      const tohandler = data

      // Both mobilePhone and mobilePhoneCountry must be set
      if (!data.mobilePhoneCountry || !data.mobilePhone) {
        setPhoneErrorState(t('validation.phoneNumberAndCountryCode'))
        return
      }
      // Phone checks passed, clear error message
      setPhoneErrorState('')

      if (
        !data.nationalIdNumber &&
        !data.passportNumber &&
        !data.passportNationality
      ) {
        // The user has not entered a national ID number nor passport information.
        // In this case the user should not be allowed to send in the registration
        setIdErrorState(t('validation.nationalIdOrPassport'))
        return
      }

      // Users should only input NIN or passport
      if (data.passportNumber && data.nationalIdNumber) {
        setIdErrorState(t('validation.doubleIdentity'))
        return
      }
      // Reset passportNationality if NIN is set and passport is empty
      if (
        data.nationalIdNumber &&
        !data.passportNumber &&
        data.passportNationality
      ) {
        setValue('passportNationality', '')
        setPassportNationality('')
        tohandler.passportNationality = ''
      }

      // if one on the passport fields are set, check that both are set
      if (
        (data.passportNumber && !data.passportNationality) ||
        (!data.passportNumber && data.passportNationality)
      ) {
        setIdErrorState(t('validation.passportNationalityAndNumber'))
        return
      }
      setIdErrorState('')

      console.log('register submit errors', errors)

      if (!Object.keys(errors).length) {
        nextHandler(tohandler)
      }
    }
    const onSubmit = handleSubmit<GuestRegisterData>(submit)

    const handlePassportNationalityChange = (event: SelectChangeEvent) => {
      if (event.target.value) {
        const passportValue = event.target.value as string
        setValue('passportNationality', passportValue)
        setPassportNationality(passportValue)
      }
    }

    const handleCountryCodeChange = (event: SelectChangeEvent) => {
      if (event.target.value) {
        const countryCodeType = event.target.value as CountryCode
        setCountryCode(countryCodeType)
        setValue('mobilePhoneCountry', countryCodeType)
      } else {
        setCountryCode(undefined)
      }
    }

    const handleMobilePhoneChange = (value: any) => {
      if (countryCode) {
        // The country code and the rest of the mobile number are in two fields, so cannot
        // register the field directly in form, but need to have extra logic defined
        // to combine the values before writing them to the form handling

        const phoneNumberWithCountryCode = `+${getCountryCallingCode(
          countryCode as CountryCode
        )}${value.target.value}`
        const isValidPhoneNumber = isValidMobilePhoneNumber(
          phoneNumberWithCountryCode
        )

        if (isValidPhoneNumber === true) {
          setValue('mobilePhone', value.target.value)
          clearErrors('mobilePhone')
        } else {
          setError('mobilePhone', {
            type: 'manual',
            message: isValidPhoneNumber || undefined,
          })
        }
      }

      setValue('mobilePhone', value.target.value)
    }

    const handleGenderChange = (event: SelectChangeEvent) => {
      if (event.target.value) {
        setGender(event.target.value)
        setValue('gender', event.target.value)
      } else {
        setGender('')
        setValue('gender', null)
      }
    }

    const today = new Date()
    const minBirthDate = subYears(100)(today)
    const maxBirthDate = subYears(1)(today)

    useEffect(() => {
      if (registerData?.passportNationality) {
        setPassportNationality(registerData.passportNationality)
      }
      if (registerData?.mobilePhoneCountry) {
        setCountryCode(registerData.mobilePhoneCountry)
      }
    }, [registerData])

    register('mobilePhoneCountry')
    register('passportNationality')

    useImperativeHandle(ref, () => ({ doSubmit: () => onSubmit() }))

    const passportCountries = Object.keys(getAlpha2Codes())
      .map((countryAlphaCode: string) => {
        const countryTuple: [string, string] = [
          countryAlphaCode,
          getName(countryAlphaCode, i18n.language),
        ]
        return countryTuple
      })
      .filter(
        (countryTuple: [string, string]) =>
          // All countries are expected to have a name, this filtering
          // is here to make some tests run in an environment where the
          // internationalization is not set up
          countryTuple[1] !== undefined
      )
      .sort(
        (countryTuple1: [string, string], countryTuple2: [string, string]) =>
          countryTuple1[1].localeCompare(countryTuple2[1])
      )

    const inviteOrIdPorten =
      initialGuestData.authentication_method === AuthenticationMethod.Invite ||
      initialGuestData.authentication_method === AuthenticationMethod.IdPorten

    // There is no name coming from ID-porten so allow the name to be editable
    const formSetup: FormSetup = {
      allowFirstNameEditable:
        inviteOrIdPorten || initialGuestData.first_name.length === 0,
      allowLastNameEditable:
        inviteOrIdPorten || initialGuestData.last_name.length === 0,
      // If there is a national ID number (presumably from Feide or ID porten),
      // already present, then do not allow the user to change the data
      // in the identifier fields
      disableIdentifierFields:
        (initialGuestData.authentication_method ===
          AuthenticationMethod.Feide ||
          initialGuestData.authentication_method ===
            AuthenticationMethod.IdPorten) &&
        initialGuestData.fnr !== null &&
        initialGuestData.fnr?.length !== 0,
    }

    const sortedPhoneCountries = getCountries()
      .map((country: CountryCode) => {
        // Make a tuple including the name to avoid
        // having to get it again further down
        const countryTuple: [CountryCode, string, CountryCallingCode] = [
          country,
          getName(country, i18n.language),
          getCountryCallingCode(country),
        ]
        return countryTuple
      })
      .filter(
        // A few country codes do not have a country name. Assuming
        // these are not needed and filtering them out to make the
        // list look nicer
        (countryTuple: [CountryCode, string, CountryCallingCode]) =>
          countryTuple[1] !== undefined
      )
      .sort(
        (
          country1: [CountryCode, string, CountryCallingCode],
          country2: [CountryCode, string, CountryCallingCode]
        ) => country1[1].localeCompare(country2[1])
      )

    return (
      <>
        <Box sx={{ maxWidth: '30rem' }}>
          <Typography
            variant="h2"
            sx={{
              paddingTop: '1rem',
              paddingBottom: '1rem',
            }}
          >
            {t('guestRegisterWizardText.yourContactInformation')}
          </Typography>
          <Typography>
            {t('guestRegisterWizardText.contactInformationDescription')}
          </Typography>
          <Divider sx={{ marginBottom: '2rem', border: '1px solid' }} />
          <form onSubmit={onSubmit}>
            <Stack spacing={2}>
              <Controller
                name="firstName"
                control={control}
                rules={{
                  required: t('common:validation.firstNameRequired').toString(),
                }}
                render={({ field: { onChange, value } }) => (
                  <TextField
                    id="firstName"
                    label={t('input.firstName')}
                    value={value}
                    onChange={onChange}
                    error={!!errors.firstName}
                    helperText={errors.firstName && errors.firstName.message}
                    disabled={!formSetup.allowFirstNameEditable}
                    inputProps={{
                      'data-testid': 'first-name-input-text',
                    }}
                  />
                )}
              />

              <Controller
                name="lastName"
                control={control}
                rules={{
                  required: t('common:validation.lastNameRequired').toString(),
                }}
                render={({ field: { onChange, value } }) => (
                  <TextField
                    id="lastName"
                    label={t('input.lastName')}
                    value={value}
                    onChange={onChange}
                    error={!!errors.lastName}
                    helperText={errors.lastName && errors.lastName.message}
                    disabled={!formSetup.allowLastNameEditable}
                    inputProps={{
                      'data-testid': 'last-name-input-text',
                    }}
                  />
                )}
              />

              {showGenderFieldForGuest && (
                <Select
                  sx={{
                    maxHeight: '2.5rem',
                    minWidth: '5rem',
                    marginRight: '0.5rem',
                  }}
                  labelId="gender-select"
                  id="gender-select-id"
                  displayEmpty
                  onChange={handleGenderChange}
                  value={gender}
                  renderValue={(selected: any) => {
                    if (!selected) {
                      return t('input.gender')
                    }
                    return t(`input.${selected}`)
                  }}
                >
                  {/* Keep it simple and hardcode the gender values */}
                  <MenuItem disabled value="">
                    {t('input.gender')}
                  </MenuItem>
                  <MenuItem value="male">{t('input.male')}</MenuItem>
                  <MenuItem value="female">{t('input.female')}</MenuItem>
                </Select>
              )}

              <Controller
                name="dateOfBirth"
                control={control}
                rules={{
                  required: t(
                    'common:validation.dateOfBirthRequired'
                  ).toString(),
                }}
                render={({ field }) => (
                  <DatePicker
                    mask="____-__-__"
                    label={t('input.dateOfBirth')}
                    // If value is set to undefined the birth date is set to today. Using null makes the field blank
                    value={field.value ?? null}
                    minDate={minBirthDate}
                    maxDate={maxBirthDate}
                    inputFormat="yyyy-MM-dd"
                    onChange={(value) => {
                      field.onChange(value)
                    }}
                    renderInput={(params) => (
                      <TextField
                        data-testid="date-of-birth-input-text"
                        // FIXME: works, but color is wrong
                        // error={!!errors.dateOfBirth}
                        // helperText={errors.dateOfBirth && errors.dateOfBirth.message}
                        {...params}
                      />
                    )}
                  />
                )}
              />
              {errors.dateOfBirth && (
                <Typography color="error">
                  {errors.dateOfBirth.message}
                </Typography>
              )}

              <TextField
                id="email"
                label={t('input.email')}
                value={initialGuestData.email || ''}
                disabled
              />

              {/* Only show the Feide ID field if the value is present */}
              {initialGuestData.feide_id && (
                <TextField
                  id="feide_id"
                  label={t('feideId')}
                  value={initialGuestData.feide_id}
                  disabled
                />
              )}

              {/* Box with phone country code and mobile phone */}
              <Box
                sx={{
                  display: 'flex',
                  flexDirection: 'row',
                }}
              >
                <Select
                  sx={{
                    maxHeight: '2.5rem',
                    minWidth: '5rem',
                    marginRight: '0.5rem',
                  }}
                  labelId="phone-number-select"
                  id="phone-number-select"
                  displayEmpty
                  onChange={handleCountryCodeChange}
                  label={t('input.countryCallingCode')}
                  value={countryCode ? countryCode.toString() : ''}
                  renderValue={(selected: any) => {
                    if (!selected || selected.length === 0) {
                      return t('input.countryCallingCode')
                    }
                    return `${getName(
                      selected,
                      i18n.language
                    )} (${getCountryCallingCode(selected as CountryCode)})`
                  }}
                  data-testid="phone-country-code-select"
                  inputProps={{
                    'data-testid': 'phone-country-code-select-inner',
                  }}
                >
                  <MenuItem disabled value="">
                    {t('input.countryCallingCode')}
                  </MenuItem>
                  {sortedPhoneCountries.map((country) => (
                    <MenuItem key={country[0]} value={country[0]}>
                      {country[1]} ({country[2]})
                    </MenuItem>
                  ))}
                </Select>

                <Controller
                  name="mobilePhone"
                  control={control}
                  rules={{
                    required: true,
                  }}
                  render={({ field }) => (
                    <TextField
                      sx={{ flexGrow: 2 }}
                      label={t('input.mobilePhone')}
                      error={!!errors.mobilePhone}
                      value={field.value ?? ''}
                      helperText={
                        errors.mobilePhone && errors.mobilePhone.message
                      }
                      onChange={handleMobilePhoneChange}
                    />
                  )}
                />
              </Box>
              {phoneErrorState && (
                <Typography color="error">{phoneErrorState}</Typography>
              )}

              <Typography variant="h2" sx={{ paddingTop: '1rem' }}>
                {t('guestRegisterWizardText.identityHeader')}
              </Typography>
              <Typography>
                <Trans i18nKey="common:guestRegisterWizardText.identityBody">
                  Enter national identity number if you have one.
                  <strong>Otherwise</strong> use passport information.
                </Trans>
              </Typography>
              <Divider sx={{ marginBottom: '1rem', border: '1px solid' }} />
              {/* The guest should fill in one of national ID number or passport number */}
              <Controller
                name="nationalIdNumber"
                control={control}
                rules={{
                  // It is not required that the national ID number be filled in, the guest may not have
                  // one, so allow empty values for the validation to pass. Note that both "fødselsnummer" and
                  // D-number are allowed as input
                  validate: (value) => isValidFnr(value, true),
                }}
                render={({ field }) => (
                  <TextField
                    id="nationalIdNumber"
                    label={t('input.nationalIdNumber')}
                    error={!!errors.nationalIdNumber}
                    value={field.value}
                    onChange={field.onChange}
                    helperText={
                      errors.nationalIdNumber && errors.nationalIdNumber.message
                    }
                    disabled={formSetup.disableIdentifierFields}
                    inputProps={{
                      'data-testid': 'national-id-number-input',
                    }}
                  />
                )}
              />
              <Box
                sx={{
                  display: 'flex',
                  flexDirection: 'row',
                }}
              >
                <Select
                  sx={{
                    maxHeight: '2.5rem',
                    minWidth: '5rem',
                    marginRight: '0.5rem',
                  }}
                  id="passport-nationality-id"
                  labelId="passport-nationality-label"
                  label={t('input.passportNationality')}
                  displayEmpty
                  value={passportNationality ?? ''}
                  onChange={handlePassportNationalityChange}
                  renderValue={(selected: any) => {
                    if (!selected || selected.length === 0) {
                      return t('input.passportNationality')
                    }
                    return selected
                  }}
                  disabled={formSetup.disableIdentifierFields}
                  inputProps={{
                    'data-testid': 'passport-nationality-input',
                  }}
                >
                  <MenuItem disabled value="">
                    {t('input.passportNationality')}
                  </MenuItem>
                  {passportCountries.map((countryTuple) => (
                    <MenuItem key={countryTuple[1]} value={countryTuple[0]}>
                      {`${countryTuple[1]} (${countryTuple[0]})`}
                    </MenuItem>
                  ))}
                </Select>
                <Controller
                  name="passportNumber"
                  control={control}
                  render={({ field }) => (
                    <TextField
                      id="passportNumber"
                      inputProps={{
                        'data-testid': 'passport_number_input',
                      }}
                      value={field.value}
                      label={t('input.passportNumber')}
                      onChange={field.onChange}
                      disabled={formSetup.disableIdentifierFields}
                    />
                  )}
                />
              </Box>
              {idErrorState && (
                <Typography color="error">{idErrorState}</Typography>
              )}

              <Typography variant="h2" sx={{ paddingTop: '1rem' }}>
                {t('guestRegisterWizardText.yourGuestPeriod')}
              </Typography>
              <Typography>
                {t('guestRegisterWizardText.guestPeriodDescription')}
              </Typography>
              <Divider sx={{ marginBottom: '1rem', border: '1px solid' }} />
              <TextField
                id="ou-unit"
                value={
                  i18n.language === 'en'
                    ? initialGuestData.ou_name_en
                    : initialGuestData.ou_name_nb
                }
                label={t('ou')}
                disabled
              />

              <TextField
                id="roleType"
                label={t('input.roleType')}
                value={
                  i18n.language === 'en'
                    ? initialGuestData.role_name_en
                    : initialGuestData.role_name_nb
                }
                disabled
              />

              <TextField
                id="rolePeriod"
                label={t('period')}
                value={`${initialGuestData.role_start} - ${initialGuestData.role_end}`}
                disabled
              />

              {displayContactAtUnitGuestInput && (
                <TextField
                  id="contactPersonUnit"
                  label={t('input.contactPersonUnit')}
                  value={initialGuestData.contact_person_unit}
                  disabled
                />
              )}
            </Stack>
          </form>
        </Box>
      </>
    )
  }
)

export default GuestRegisterStep