import React, { useCallback, useEffect, useRef, useState } from 'react'
import apiFetch from '@wordpress/api-fetch'
import {
  Button,
  Snackbar,
  Spinner,
  Notice,
  DropdownMenu,
  MenuGroup,
  MenuItem,
  SelectControl,
  __experimentalHeading as Heading,
} from '@wordpress/components'
import { Card, Text } from '@wordpress/ui'
import {
  BookingRequest,
  MybringBooking,
  MybringCustomerAndServices,
  Order,
  OrderItem,
  ServiceDetails,
} from './model/models'
import CustomerSelector from './components/CustomerSelector'
import { useI18n } from '@wordpress/react-i18n'
import {
  pages,
  undo,
  redo,
  reusableBlock,
  Icon,
  search,
} from '@wordpress/icons'
import { useEnableWpCompatOverlaySlot } from '@wordpress/ui'
import OrderListItem from './components/OrderListItem'
import BookedOrderListItem from './components/BookedOrderListItem'
import { sprintf } from '@wordpress/i18n'
import CustomsInformationHsCodes from './components/CustomsInformationHsCodes'

const OrderList = () => {
  const [orders, setOrders] = useState<Array<Order>>([])
  const [customers, setCustomers] = useState<Array<MybringCustomerAndServices>>(
    []
  )
  const [customerNumber, setCustomerNumber] = useState<string | undefined>()
  const [bookingStatus, setBookingStatus] = useState<Record<string, string>>({})
  const [inProgress, setInProgress] = useState<boolean>(false)
  const [loading, setLoading] = useState<boolean>(false)
  const [labelFailure, setLabelFailure] = useState(false)
  const [allInProgress, setAllInProgress] = useState<boolean>(false)
  const [waybillsFailure, setWaybillsFailure] = useState(false)
  const [labelsInProgress, setLabelsInProgress] = useState(false)
  const [waybillsInProgress, setWaybillsInProgress] = useState(false)
  const [returnLabelsInProgress, setReturnLabelsInProgress] = useState(false)
  const [displayHsCodeEditor, setDisplayHsCodeEditor] = useState<boolean>(false)
  const [itemsMissingHsCode, setItemsMissingHsCode] = useState<
    Array<OrderItem>
  >([])
  const [labelSize, setLabelSize] = useState<'ALL' | 'STANDARD' | 'MAILBOX'>(
    'ALL'
  )
  const [menuOpen, setMenuOpen] = useState(false)
  const [availableServices, setAvailableServices] = useState<
    Array<ServiceDetails>
  >([])
  const { __, _n } = useI18n()
  const orderButtonRef = useRef<HTMLButtonElement | null>(null)
  const labelButtonRef = useRef<HTMLButtonElement | null>(null)
  const [focusOrderButton, setFocusOrderButton] = useState(false)
  const [focusLabelButton, setFocusLabelButton] = useState(false)
  useEnableWpCompatOverlaySlot()

  useEffect(() => {
    if (focusOrderButton && orderButtonRef.current) {
      orderButtonRef.current.focus()
      setFocusOrderButton(false)
    }
  }, [focusOrderButton])

  useEffect(() => {
    if (focusLabelButton && labelButtonRef.current) {
      labelButtonRef.current.focus()
      setFocusLabelButton(false)
    }
  }, [focusLabelButton])

  const fetchCustomers = useCallback(async () => {
    const response: MybringCustomerAndServices[] = await apiFetch({
      path: '/posten-bring-checkout/customers',
    })
    if (response) {
      setCustomers(response)
      setCustomerNumber(current => {
        if (current || response.length === 0) {
          return current
        }
        const defaultCustomer = response.find(it => it.defaultCustomer)
        return defaultCustomer?.customerNumber ?? response[0].customerNumber
      })
    }
  }, [])

  const fetchOrders = async (params: string) => {
    setLoading(true)
    const response: Order[] = await apiFetch<Array<Order>>({
      path: '/posten-bring-checkout/bookings' + (params ? '?' + params : ''),
    }).finally(() => setLoading(false))
    if (response) {
      setOrders(response)
      setItemsMissingHsCode([
        ...new Map(
          response
            .filter(order => order.nvit)
            .flatMap(order =>
              order.items.filter(item => !item.hs_code || item.hs_code === '')
            )
            .map(item => [item.product_id, item])
        ).values(),
      ])
      setFocusOrderButton(true)
    }
  }

  const fetchAvailableServices = async () => {
    const response: ServiceDetails[] = await apiFetch<Array<ServiceDetails>>({
      path: '/posten-bring-checkout/services',
    })
    if (response) setAvailableServices(response)
  }

  const updateOrders = useCallback(() => {
    const orderIdsString = sessionStorage.getItem('posten_order_ids')
    if (orderIdsString) {
      const orderIdsArray = orderIdsString.split(',')
      const queryParamsArray = orderIdsArray.map(id => `orderIds[]=${id}`)
      const params = queryParamsArray.join('&')
      fetchOrders(params)
    }
  }, [])

  useEffect(() => {
    fetchCustomers()
    updateOrders()
    fetchAvailableServices()
    const handleSessionStorageChange = () => {
      setOrders([])
      updateOrders()
    }

    window.addEventListener(
      'posten-order-ids-changed',
      handleSessionStorageChange
    )

    return () => {
      window.removeEventListener(
        'posten-order-ids-changed',
        handleSessionStorageChange
      )
    }
  }, [fetchCustomers, updateOrders])

  const handleBooking = async () => {
    if (!orders.length) {
      return
    }

    setInProgress(true)

    const newBookingStatus = { ...bookingStatus }
    const requests = orders
      .filter(order => (order.mybring_bookings?.length || 0) === 0)
      .map(async order => {
        const data = {
          orderId: order.order_id,
          testIndicator: false,
          selectedShippingOption: order.order_shipping_option,
          customerNumber,
          orderRef: order.order_id,
          packages: [
            {
              weightInKg: order.total_weight / 1000,
            },
          ],
        } as BookingRequest
        if (
          !order.order_validation_errors.some(value => value.bookingDisabled) &&
          order.order_shipping_option
        ) {
          try {
            const result = await apiFetch<Array<MybringBooking>>({
              path: '/posten-bring-checkout/bookings?orderId=' + order.order_id,
              method: 'POST',
              headers: {
                'Content-Type': 'application/json',
              },
              body: JSON.stringify(data),
            })

            setOrders(prevOrders =>
              prevOrders.map(prevOrder => {
                if (prevOrder.order_id === order.order_id) {
                  return {
                    ...prevOrder,
                    mybring_bookings: result,
                  }
                }
                return prevOrder
              })
            )

            newBookingStatus[order.order_id] = 'success'
            // eslint-disable-next-line @typescript-eslint/no-unused-vars
          } catch (error: unknown) {
            newBookingStatus[order.order_id] = 'error'
          }

          const newStatus = { ...newBookingStatus }
          setBookingStatus(newStatus)
        }
      })

    await Promise.all(requests)
    setInProgress(false)
    setFocusLabelButton(true)
  }

  const mapServiceId = (orderShippingOption: string | undefined) => {
    if (!orderShippingOption) {
      return (
        <Notice status="warning" isDismissible={false} politeness="assertive">
          {__('No shipping option selected', 'posten-bring-checkout')}
        </Notice>
      )
    }
    return (
      availableServices &&
      availableServices.find(
        service => service.service_id === orderShippingOption
      )?.name
    )
  }

  const onSave = (orderId: string, editedOrder?: Order) => {
    setOrders(
      orders.map(order =>
        order.order_id === orderId ? editedOrder || order : order
      )
    )
  }

  const bookableOrders = orders.filter(
    order =>
      (order.mybring_bookings?.length || 0) === 0 &&
      bookingStatus[order.order_id] !== 'success'
  )

  const bookedOrders = orders
    .filter(
      order =>
        (order.mybring_bookings?.length || 0) > 0 &&
        order.order_shipping_option &&
        order.order_shipping_option !== ''
    )
    .filter(order => {
      if (labelSize === 'MAILBOX') {
        return order.order_shipping_option === '3584'
      } else if (labelSize === 'STANDARD') {
        return order.order_shipping_option !== '3584'
      } else {
        return true
      }
    })

  const getLabels = async (labelType: string) => {
    setLabelFailure(false)
    const newTab = window.open('', '_blank')
    const orderIds = orders
      .filter(
        order =>
          bookingStatus[order.order_id] === 'success' ||
          (order.mybring_bookings?.length || 0) > 0
      )
      .filter(order => {
        if (labelSize === 'MAILBOX') {
          return order.order_shipping_option === '3584'
        } else if (labelSize === 'STANDARD') {
          return order.order_shipping_option !== '3584'
        } else {
          return true
        }
      })
      .map(order => order.order_id)
      .join(',')
    const response = await apiFetch<Response, false>({
      path: `/posten-bring-checkout/labels?orderIds=${orderIds}&labelType=${labelType}`,
      method: 'GET',
      headers: {
        Accept: 'application/pdf',
      },
      parse: false,
    })
      .catch(() => {
        setLabelFailure(true)
      })
      .finally(() => {
        setAllInProgress(false)
        setLabelsInProgress(false)
        setReturnLabelsInProgress(false)
      })

    setLabelsInProgress(false)
    setReturnLabelsInProgress(false)
    if (response?.ok) {
      const blob = await response.blob()
      const url = window.URL.createObjectURL(
        new Blob([blob], { type: 'application/pdf' })
      )

      if (newTab) {
        newTab.location.href = url
      }
    }
  }

  const getWaybills = async () => {
    setWaybillsFailure(false)
    const newTab = window.open('', '_blank')
    const orderIds = orders
      .filter(
        order =>
          order.mybring_bookings.find(b => b.waybillUrl) &&
          (bookingStatus[order.order_id] === 'success' ||
            (order.mybring_bookings?.length || 0) > 0)
      )
      .map(order => order.order_id)
      .join(',')
    const response = await apiFetch<Response, false>({
      path: `/posten-bring-checkout/waybills?orderIds=${orderIds}`,
      method: 'GET',
      headers: {
        Accept: 'application/pdf',
      },
      parse: false,
    }).catch(() => {
      setWaybillsFailure(true)
    })

    setWaybillsInProgress(false)
    if (response?.ok) {
      const blob = await response.blob()
      const url = window.URL.createObjectURL(
        new Blob([blob], { type: 'application/pdf' })
      )

      if (newTab) {
        newTab.location.href = url
      }
    }
  }

  return (
    <div className="mass-booking">
      {displayHsCodeEditor ? (
        <div className="flex flex-dir-row justify-cc align-ic customs-container">
          <CustomsInformationHsCodes
            itemsMissingHsCode={itemsMissingHsCode}
            onCancel={() => setDisplayHsCodeEditor(false)}
            onSave={() => {
              setDisplayHsCodeEditor(false)
              updateOrders()
            }}
          />
        </div>
      ) : (
        <>
          {(loading || bookableOrders.length > 0) && (
            <Card.Root className="mbl">
              <Card.Header>
                <Card.Title>
                  <Heading level={1} weight={200} style={{ margin: '0' }}>
                    {sprintf(
                      _n(
                        '%d order awaiting shipment booking',
                        '%d orders awaiting shipment booking',
                        bookableOrders.length,
                        'posten-bring-checkout'
                      ),
                      bookableOrders.length
                    )}
                  </Heading>
                </Card.Title>
              </Card.Header>
              <Card.Content>
                <div className="flex flex-dir-row justify-ce align-ic mbs">
                  {itemsMissingHsCode.length > 0 && (
                    <Button
                      role="button"
                      className="mrs"
                      variant="secondary"
                      __next40pxDefaultSize={true}
                      onClick={() => setDisplayHsCodeEditor(true)}
                      onKeyDown={e => {
                        if (e.key === 'Enter') {
                          setDisplayHsCodeEditor(true)
                        }
                      }}
                    >
                      {_n(
                        'Add HS code',
                        'Add HS codes',
                        itemsMissingHsCode.length,
                        'posten-bring-checkout'
                      )}
                    </Button>
                  )}
                  <CustomerSelector
                    customers={customers}
                    selectedCustomer={customerNumber}
                    onSelect={selectedCustomerNumber =>
                      setCustomerNumber(selectedCustomerNumber)
                    }
                  />
                  <Button
                    ref={orderButtonRef}
                    role="button"
                    variant={'primary'}
                    className="mls"
                    __next40pxDefaultSize={true}
                    data-testid="book-transport"
                    onClick={handleBooking}
                    isBusy={inProgress}
                    onKeyDown={e => {
                      if (e.key === 'Enter') {
                        handleBooking()
                      }
                    }}
                    disabled={
                      !customerNumber ||
                      bookableOrders.length ===
                        bookableOrders.filter(order =>
                          order.order_validation_errors.find(
                            e => e.bookingDisabled
                          )
                        ).length ||
                      (bookedOrders.length > 0 && bookableOrders.length === 0)
                    }
                  >
                    {__('Book transport', 'posten-bring-checkout')}
                  </Button>
                </div>
                <div className="order-list">
                  <ul>
                    {loading && (
                      <div className="flex flex-dir-row align-ic justify-cc mtxl">
                        <Spinner style={{ height: '40px', width: '40px' }} />
                      </div>
                    )}
                    {bookableOrders.map(order => (
                      <OrderListItem
                        key={order.order_id}
                        order={order}
                        mapServiceId={mapServiceId}
                        customers={customers}
                        customerNumber={customerNumber}
                        onSave={onSave}
                        error={bookingStatus[order.order_id]}
                      />
                    ))}
                  </ul>
                </div>
              </Card.Content>
            </Card.Root>
          )}
          <Card.Root>
            <Card.Header>
              <Card.Title>
                <Heading level={1} weight={200} style={{ margin: '0' }}>
                  {sprintf(
                    _n(
                      '%d order ready to be shipped',
                      '%d orders ready to be shipped',
                      bookedOrders.length,
                      'posten-bring-checkout'
                    ),
                    bookedOrders.length
                  )}
                </Heading>
              </Card.Title>
            </Card.Header>
            <Card.Content>
              <div
                className={`flex flex-dir-row align-ic mbs ${
                  labelFailure || waybillsFailure ? 'justify-csb' : 'justify-ce'
                }`}
              >
                {labelFailure && (
                  <Snackbar
                    className="mts"
                    politeness="assertive"
                    onRemove={() => {
                      setLabelFailure(false)
                    }}
                  >
                    {__(
                      'An error occurred while generating labels. Please try again',
                      'posten-bring-checkout'
                    )}
                  </Snackbar>
                )}
                {waybillsFailure && (
                  <Snackbar
                    className="mts"
                    politeness="assertive"
                    onRemove={() => {
                      setWaybillsFailure(false)
                    }}
                  >
                    {__(
                      'An error occurred while generating waybills. Please try again',
                      'posten-bring-checkout'
                    )}
                  </Snackbar>
                )}
                <div className="flex flex-dir-row align-ife justify-ce">
                  <SelectControl
                    onChange={setLabelSize}
                    className="mrs"
                    label={__('Filter by label size', 'posten-bring-checkout')}
                    value={labelSize}
                    __next40pxDefaultSize={true}
                    options={[
                      {
                        label: __('All label sizes', 'posten-bring-checkout'),
                        value: 'ALL',
                      },
                      {
                        label: __('Standard size', 'posten-bring-checkout'),
                        value: 'STANDARD',
                      },
                      {
                        label: __(
                          'Mailbox parcel (domestic Norway) size',
                          'posten-bring-checkout'
                        ),
                        value: 'MAILBOX',
                      },
                    ]}
                  />
                  <DropdownMenu
                    label={__('Print labels', 'posten-bring-checkout')}
                    open={menuOpen}
                    onToggle={setMenuOpen}
                    icon={null}
                    toggleProps={{
                      ref: labelButtonRef,
                      __next40pxDefaultSize: true,
                      type: 'button',
                      variant:
                        bookableOrders.length === 0 ? 'primary' : 'secondary',
                      disabled: inProgress || bookedOrders.length === 0,
                      children: __('Print labels', 'posten-bring-checkout'),
                    }}
                  >
                    {({ onClose }) => (
                      <MenuGroup>
                        <MenuItem
                          icon={
                            allInProgress ? (
                              <Spinner
                                style={{
                                  height: '16px',
                                  width: '16px',
                                  margin: '0 4px',
                                }}
                              />
                            ) : (
                              reusableBlock
                            )
                          }
                          onClick={() => {
                            setAllInProgress(true)
                            getLabels('ALL').finally(() => onClose())
                          }}
                        >
                          {__('Print all', 'posten-bring-checkout')}
                        </MenuItem>
                        {bookedOrders.find(order =>
                          order.mybring_bookings.find(b => b.returnLabelUrl)
                        ) ? (
                          <>
                            <MenuItem
                              icon={
                                labelsInProgress ? (
                                  <Spinner
                                    style={{
                                      height: '16px',
                                      width: '16px',
                                      margin: '0 4px',
                                    }}
                                  />
                                ) : (
                                  redo
                                )
                              }
                              onClick={() => {
                                setLabelsInProgress(true)
                                getLabels('LABEL').finally(() => onClose())
                              }}
                            >
                              {__('Print labels', 'posten-bring-checkout')}
                            </MenuItem>
                            <MenuItem
                              icon={
                                returnLabelsInProgress ? (
                                  <Spinner
                                    style={{
                                      height: '16px',
                                      width: '16px',
                                      margin: '0 4px',
                                    }}
                                  />
                                ) : (
                                  undo
                                )
                              }
                              onClick={() => {
                                setReturnLabelsInProgress(true)
                                getLabels('RETURN').finally(() => onClose())
                              }}
                            >
                              {__(
                                'Print return labels',
                                'posten-bring-checkout'
                              )}
                            </MenuItem>
                          </>
                        ) : null}
                        {bookedOrders.find(order =>
                          order.mybring_bookings.find(b => b.waybillUrl)
                        ) && (
                          <MenuItem
                            icon={
                              waybillsInProgress ? (
                                <Spinner
                                  style={{
                                    height: '16px',
                                    width: '16px',
                                    margin: '0 4px',
                                  }}
                                />
                              ) : (
                                pages
                              )
                            }
                            onClick={() => {
                              setWaybillsInProgress(true)
                              getWaybills().finally(() => onClose())
                            }}
                          >
                            {__('Print waybills', 'posten-bring-checkout')}
                          </MenuItem>
                        )}
                      </MenuGroup>
                    )}
                  </DropdownMenu>
                </div>
              </div>
              <div className="order-list">
                {bookedOrders.length > 0 ? (
                  <ul>
                    {bookedOrders.map(order => (
                      <BookedOrderListItem order={order} key={order.order_id} />
                    ))}
                  </ul>
                ) : (
                  <>
                    {loading ? (
                      <div className="flex flex-dir-row align-ic justify-cc mtxl">
                        <Spinner style={{ height: '40px', width: '40px' }} />
                      </div>
                    ) : (
                      <div className="flex mtl flex-dir-row align-ic justify-cc">
                        <div className="flex flex-dir-col align-ic justify-cc">
                          <Icon icon={search} size={100} />
                          <Text variant="body-xl">
                            {__('No orders found', 'posten-bring-checkout')}
                          </Text>
                        </div>
                      </div>
                    )}
                  </>
                )}
              </div>
            </Card.Content>
          </Card.Root>
        </>
      )}
    </div>
  )
}

export default OrderList
