import React, { useState } from 'react'
import { FormInstance } from 'antd'
import { Pill } from 'components/Pill'
import { Select, Option } from 'components/Select'

import moment, { Moment } from 'moment'
import {
  Timezone,
  TimezoneSearchResult,
  TimezoneSearchType,
  searchTimezones,
  TimezoneMap
} from './utilities/timezone'
import timezoneMapData from './utilities/timezoneMap.json'
import { noop } from 'utils/noop'

import classnames from 'classnames'
import './TimezoneSelect.scss'

const createListMessage = (items: string[] = []) => {
  if (items.length === 0) {
    return ''
  }

  const othersLength = items.length - 1
  const [firstItem] = items

  if (othersLength === 0) {
    return firstItem
  }

  const othersSuffix = othersLength === 1 ? 'other' : 'others'
  return `${firstItem} and ${othersLength} ${othersSuffix}`
}

const toTwoDigit = (number: number) =>
  number.toLocaleString(undefined, { minimumIntegerDigits: 2 })

/**
 *
 * @param {number} offset The offset in milliseconds
 * @returns
 */
export const offsetToDescriptiveOffset = (offset: number): string => {
  const sign = offset < 0 ? '-' : '+'
  const absoluteOffset = Math.abs(offset)

  const minInMs = 1000 * 60
  const hourInMs = minInMs * 60

  const hours = Math.floor(absoluteOffset / hourInMs)
  const mins = Math.floor((absoluteOffset / minInMs) % 60)

  return `UTC ${sign}${toTwoDigit(hours)}:${toTwoDigit(mins)}`
}

const createTimezoneOptionContent = (
  timezoneResult: TimezoneSearchResult,
  date: Moment
) => {
  const { timezone, searchType, matches } = timezoneResult

  const dstAwareTimezone = moment.tz(date, timezone.primaryName)
  const abbreviation = dstAwareTimezone.zoneAbbr()
  const dstOffset = dstAwareTimezone.utcOffset()

  const { descriptiveName } = timezone
  const descriptiveOffset = offsetToDescriptiveOffset(dstOffset * 1000 * 60)

  const isMatchMessage =
    matches && searchType !== TimezoneSearchType.DESCRIPTIVE_NAME
  const resultsMessage = isMatchMessage
    ? `Matches ${createListMessage(matches)}`
    : ''

  return (
    <div className="timezone-select--option">
      <Pill
        className="timezone-select--option-abbreviation"
        text={abbreviation}
      />
      <span className="timezone-select--option-timezone">
        {descriptiveName}
      </span>
      <span className="timezone-select--option-alternatives">
        {resultsMessage}
      </span>
      <span className="timezone-select--option-offset">
        {descriptiveOffset}
      </span>
    </div>
  )
}

const createTimezoneSelection = (timezoneName: string, date: Moment) =>
  createTimezoneOptionContent({ timezone: timezoneMap[timezoneName] }, date)

const timezoneMap = timezoneMapData as TimezoneMap

export const primaryTimezones = Object.values(timezoneMap).filter(
  timezone => timezone.isPrimary
)
const sortedTimezones = primaryTimezones.sort((a, b) =>
  a.offset > b.offset ? 1 : -1
)
const sortedTimezonesAsResults = sortedTimezones.map<TimezoneSearchResult>(
  timezone => ({ timezone })
)

export interface TimezoneOptionProps {
  key: number
  timezone: Timezone
}

export interface TimezoneSelectProps {
  form?: FormInstance
  date?: Moment
  initialValue?: string
  handleChange?: (timezone?: Timezone) => void
  handleSelect: (timezone: Timezone) => void
}

export const TimezoneSelect: React.FC<TimezoneSelectProps> = ({
  form,
  date = moment(),
  initialValue,
  handleChange = noop,
  handleSelect
}) => {
  const [filteredTimezones, setFilteredTimezones] = useState(
    sortedTimezonesAsResults
  )
  const [selectedTimezone, setSelectedTimezone] = useState<string | undefined>(
    initialValue
  )

  const handleKeyUp: React.KeyboardEventHandler = event => {
    switch (event.key) {
      case 'Backspace':
      case 'Escape':
        setSelectedTimezone(undefined)
        handleChange(undefined)
    }
  }

  const handleSearch = (value: string) => {
    const timezoneResults = searchTimezones(value)

    setSelectedTimezone(undefined)
    setFilteredTimezones(timezoneResults)
  }

  const handleSelection = (primaryName: string) => {
    setSelectedTimezone(primaryName)

    const timezone = timezoneMap[primaryName]

    handleSelect(timezone)
    handleChange(timezone)

    if (form) {
      form.setFieldsValue({ timezone: primaryName })
    }
  }

  const selectClassNames = React.useMemo(
    () =>
      classnames([
        'timezone-select',
        selectedTimezone && 'timezone-select--laden'
      ]),
    [selectedTimezone]
  )

  return (
    <Select
      className={selectClassNames}
      filterOption={false}
      showSearch
      onKeyUp={handleKeyUp}
      onSearch={handleSearch}
      onSelect={handleSelection}
      value={
        selectedTimezone && createTimezoneSelection(selectedTimezone, date)
      }
    >
      {filteredTimezones.map((timezoneResult, index) => {
        const {
          timezone: { primaryName }
        } = timezoneResult
        const optionContent = createTimezoneOptionContent(timezoneResult, date)

        return (
          <Option
            key={index}
            className="timezone-select--option"
            value={primaryName}
          >
            {optionContent}
          </Option>
        )
      })}
    </Select>
  )
}
