/* eslint-disable @typescript-eslint/no-explicit-any */
import { FormikErrors, FormikValues } from 'formik'
import _flatMapDeep from 'lodash/flatMapDeep'
import _forEach from 'lodash/forEach'
import _get from 'lodash/get'
import _isArray from 'lodash/isArray'
import _isEmpty from 'lodash/isEmpty'
import _map from 'lodash/map'
import { nanoid } from 'nanoid'
import React from 'react'

import { AttributesConfig } from '../../api'
import { IGroupItem } from '../../types'
import { CONFIGS } from '../../utils'
import { combineValidators, requiredValidator } from '../../validators'
import {
  CheckboxField,
  CustomField,
  DatePickerField,
  PhoneNumberField,
  RadioGroupField,
  SelectField,
} from '../common/index'

export interface ILoggedInValues {
  emailLogged?: string;
  firstNameLogged?: string;
  lastNameLogged?: string;
  phoneLogged?: string;
}

export interface IValues {
  [key: string]: any;
}

export const getInitialValues = (
  data: any = [],
  propsInitialValues: IValues = {},
  userValues: any = {},
  ticketHoldersFields: any = {},
  ticketsQuantity: any = []
): IValues => {
  const results = _flatMapDeep(data, ({ fields }) =>
    _map(fields, ({ groupItems }) =>
      _map(groupItems, ({ name, value }) => ({ name, value }))
    )
  )

  // Add Ticket Holder default values for custom fields
  const ticketHoldersCustomFields =
    ticketHoldersFields?.fields?.find((groupItem: any) => groupItem.customFields)
      ?.groupItems || []
  const ticketHoldersCustomFieldsInitValues = {} as IValues
  const selectedTicketsCount =
    ticketsQuantity.length || localStorage.getItem('selectedTicketsQuantity') || 0

  if (!_isEmpty(ticketHoldersCustomFields)) {
    for (const customField of ticketHoldersCustomFields) {
      for (let i = 0; i < selectedTicketsCount; i++) {
        const fieldName = `${customField.name}-${i}`
        const fieldValue = customField.value
        ticketHoldersCustomFieldsInitValues[fieldName] = fieldValue
      }
    }
  }

  const initialValues: IValues = {}
  _forEach(results, groupItem => {
    const { name, value } = groupItem
    initialValues[name] = value || propsInitialValues[name] || userValues[name] || ''
  })

  // set logged in user as first ticket holder
  initialValues['holderFirstName-0'] =
    propsInitialValues.firstName || userValues.firstName || ''
  initialValues['holderLastName-0'] =
    propsInitialValues.lastName || userValues.lastName || ''
  initialValues['holderEmail-0'] = propsInitialValues.email || userValues.email || ''
  initialValues['holderPhone-0'] = propsInitialValues.phone || userValues.phone || ''

  return { ...initialValues, ...ticketHoldersCustomFieldsInitValues }
}

export const createRegisterFormData = (
  values: IValues = {},
  checkoutBody: { attributes: { [key: string]: any } },
  flagFreeTicket = false
): FormData => {
  const bodyFormData = new FormData()
  bodyFormData.append('first_name', values.firstName)
  bodyFormData.append('last_name', values.lastName)
  bodyFormData.append('email', values.email)
  bodyFormData.append('password', values.password)
  bodyFormData.append('password_confirmation', values.confirmPassword)
  bodyFormData.append(
    'client_id',
    CONFIGS.CLIENT_ID || 'e9d8f8922797b4621e562255afe90dbf'
  )
  bodyFormData.append(
    'client_secret',
    CONFIGS.CLIENT_SECRET || 'b89c191eff22fdcf84ac9bfd88d005355a151ec2c83b26b9'
  )
  bodyFormData.append('check_cart_expiration', 'true')

  _forEach(checkoutBody.attributes, (item: any, key: string) => {
    if (
      !(flagFreeTicket && ['country', 'state', 'city', 'street_address'].includes(key))
    ) {
      bodyFormData.append(key, item)
    }
  })

  return bodyFormData
}

export interface ICheckoutBody {
  attributes: {
    [key: string]: any;
  };
  data_capture?: {
    [key: string]: any;
  };
}

interface IticketHolder {
  first_name?: string;
  last_name?: string;
  phone?: string;
  email?: string;
}

export const createCheckoutDataBody = (
  ticketsQuantity: number,
  values: IValues = {},
  logedInValues: ILoggedInValues = {},
  includeDob = false
): ICheckoutBody => {
  const { firstName, lastName, holderAge, confirmEmail, confirmPassword, ...restValues } =
    values

  const holders = []
  let ticket_holders: IticketHolder[] = []

  for (let i = 0; i <= ticketsQuantity; i++) {
    const individualHolder = Object.fromEntries(
      Object.entries(values).filter(([key, _val]) => key.includes(String(i)))
    )
    holders.push(individualHolder)
  }

  const filteredHolders = holders.filter(holder => Object.entries(holder).length > 0)
  ticket_holders = filteredHolders.map((item, index) => ({
    first_name: !index
      ? item[`holderFirstName-${index}`] || logedInValues.firstNameLogged || ''
      : item[`holderFirstName-${index}`] || '',
    last_name: !index
      ? item[`holderLastName-${index}`] || logedInValues.lastNameLogged || ''
      : item[`holderLastName-${index}`] || '',
    phone: !index
      ? item[`holderPhone-${index}`] || logedInValues.phoneLogged || ''
      : item[`holderPhone-${index}`] || '',
    email: !index
      ? item[`holderEmail-${index}`] || logedInValues.emailLogged || ''
      : item[`holderEmail-${index}`] || '',
  }))

  const filteredRestValue: { [key: string]: any } = {}
  _forEach(restValues, (value, key) => {
    if (!key.includes('holder')) {
      filteredRestValue[key] = value
    }
  })

  const body: ICheckoutBody = {
    attributes: {
      ...filteredRestValue,
      email: restValues.email || logedInValues.emailLogged,
      confirm_email: restValues.email || logedInValues.emailLogged,
      first_name: firstName || logedInValues.firstNameLogged,
      last_name: lastName || logedInValues.lastNameLogged,
      ticket_holders,
    },
  }

  if (includeDob) {
    const holderAgeDate = new Date(holderAge)
    body.attributes.dob_day = holderAgeDate.getDate()
    body.attributes.dob_month = holderAgeDate.getMonth() + 1
    body.attributes.dob_year = holderAgeDate.getFullYear()
  }
  return body
}

export const getValidateFunctions = (
  element: IGroupItem,
  states: Array<{ [key: string]: any }>,
  values: FormikValues,
  errors: FormikErrors<any>
) => {
  const validationFunctions: any[] = []

  if (element.required) {
    if (element.name !== 'state' || (element.name === 'state' && states.length)) {
      validationFunctions.push(requiredValidator)
    }
  }

  if (element.onValidate) {
    validationFunctions.push(element.onValidate)
  }

  if (element.name === 'phone') {
    const invalidPhone = () =>
      errors.phone === 'Invalid phone number' ? 'Invalid phone number' : null
    validationFunctions.push(invalidPhone)
  }

  if (element.name === 'confirmEmail') {
    const isSameEmail = (confirmEmail?: string) =>
      values.email !== confirmEmail ? 'Please confirm your email address correctly' : null
    validationFunctions.push(isSameEmail)
  }

  if (element.name === 'confirmPassword') {
    const isSame = (confirmPassword?: string) =>
      values.password !== confirmPassword ? 'Password confirmation does not match' : null
    validationFunctions.push(isSame)
  }

  return combineValidators(...validationFunctions)
}

export const assingUniqueIds = (data: any): any => {
  if (_get(data[0], 'uniqueId')) {
    return data
  }

  return _map(data, (item: any) => {
    _forEach(item, (itemValue: string, key) => {
      if (_isArray(itemValue) && !itemValue.some(item => typeof item === 'string')) {
        item[key] = assingUniqueIds(itemValue)
      }
    })

    return { ...item, uniqueId: nanoid() }
  })
}

export const isRequiredField = (
  element: IGroupItem,
  configs?: null | AttributesConfig
) => {
  const { name, required } = element
  const flagRequirePhone = _get(configs, 'phone_required', false)
  const collectMandatoryWalletAddress = _get(
    configs,
    'collect_mandatory_wallet_address',
    false
  )

  if (
    required ||
    (name === 'phone' && flagRequirePhone) ||
    (name === 'data_capture[wallet_address]' && !collectMandatoryWalletAddress)
  ) {
    return true
  }

  return false
}

export const getFieldLabel = (element: IGroupItem, configs?: null | AttributesConfig) => {
  if (isRequiredField(element, configs) || React.isValidElement(element.label)) {
    return element.label
  }

  return `${element.label} (optional)`
}

export const getFieldComponent = (element: IGroupItem) => {
  const type = _get(element, 'type', 'text')

  const fieldComponentConfigs = {
    checkbox: CheckboxField,
    select: CustomField, // Temp change untill refactoring
    select_multi: SelectField,
    phone: PhoneNumberField,
    date: DatePickerField,
    radio: RadioGroupField,
    text: CustomField,
  }

  const fieldComponent = _get(fieldComponentConfigs, type, CustomField)
  return fieldComponent
}

/**
 * Renders a React component with the provided props
 * @param Component - The React component to render
 * @param props - The props to apply to the component
 * @returns JSX element with applied props
 */
export const renderComponentWithProps = <T extends Record<string, any>>(
  Component: React.ComponentType<T>,
  props: T
): React.ReactElement<T> => <Component {...props} />

export const filterBillingInfoFields = (
  fields: IGroupItem[],
  options: {
    showDOB: boolean;
    hideTtfOptIn: boolean;
    hidePhoneField: boolean;
    flagRequirePhone: boolean;
    collectMandatoryWalletAddress: boolean;
    collectMandatoryJobTitle: boolean;
    collectMandatoryBusinessCategory: boolean;
    collectMandatoryCompany: boolean;
    collectMandatoryInstagram: boolean;
    flagFreeTicket: boolean;
    hideWalletAddressField: boolean;
    hideJobTitleField: boolean;
    hideBusinessCategoryField: boolean;
    hideCompanyField: boolean;
    hideInstagramField: boolean;
  }
) => {
  const {
    showDOB,
    hideTtfOptIn,
    hidePhoneField,
    flagRequirePhone,
    collectMandatoryWalletAddress,
    collectMandatoryJobTitle,
    collectMandatoryBusinessCategory,
    collectMandatoryCompany,
    collectMandatoryInstagram,
    flagFreeTicket,
    hideWalletAddressField,
    hideJobTitleField,
    hideBusinessCategoryField,
    hideCompanyField,
    hideInstagramField,
  } = options
  return fields.filter(el => {
    if (el.name === 'holderAge' && !showDOB) {
      return false
    }
    if (el.name === 'ttf_opt_in' && hideTtfOptIn) {
      return false
    }
    if (el.name === 'phone') {
      if (!hidePhoneField) {
        el.required = flagRequirePhone
      } else {
        return false
      }
    }
    if (el.name === 'data_capture[wallet_address]') {
      if (collectMandatoryWalletAddress) {
        el.required = true
      }
    }
    if (el.name === 'data_capture[jobTitle]') {
      if (collectMandatoryJobTitle) {
        el.required = true
      }
    }
    if (el.name === 'data_capture[businessCategory]') {
      if (collectMandatoryBusinessCategory) {
        el.required = true
      }
    }
    if (el.name === 'data_capture[company]') {
      if (collectMandatoryCompany) {
        el.required = true
      }
    }
    if (el.name === 'data_capture[instagram]') {
      if (collectMandatoryInstagram) {
        el.required = true
      }
    }
    if (['street_address', 'country', 'state', 'city'].includes(el.name)) {
      if (flagFreeTicket) {
        el.required = false
        return false
      }
    }
    if (
      hideWalletAddressField &&
      (el.name === 'wallet-address-info' || el.name === 'data_capture[wallet_address]')
    ) {
      return false
    }
    if (hideJobTitleField && el.name === 'data_capture[jobTitle]') {
      return false
    }
    if (hideBusinessCategoryField && el.name === 'data_capture[businessCategory]') {
      return false
    }
    if (hideCompanyField && el.name === 'data_capture[company]') {
      return false
    }
    if (
      hideInstagramField &&
      (el.name === 'data_capture[instagram]' || el.name === 'instagram-info')
    ) {
      return false
    }
    return true
  })
}
