import React, { useCallback, useEffect, useState } from 'react'
import apiFetch from '@wordpress/api-fetch'
import bringLogo from '../../assets/img/Bring_logo.svg'
import collectPackagesImg from '../../assets/img/collect-packages.png'
import {
  Button,
  ButtonGroup,
  Snackbar,
  Spinner,
  Notice,
} from '@wordpress/components'
import {
  BookingRequest,
  MybringCustomerAndServices,
  Order,
  ServiceDetails,
  ValidationError,
} from './model/models'
import CustomerSelector from './components/CustomerSelector'
import { useI18n } from '@wordpress/react-i18n'
import { arrowRight, pages, undo } from '@wordpress/icons'
import BookedOrders from './components/BookedOrders'
import Orders from './components/Orders'

const OrderList = () => {
  const [orders, setOrders] = useState<Array<Order>>([])
  const [customers, setCustomers] = useState<Array<MybringCustomerAndServices>>(
    []
  )
  const [failedFromBooking, setFailedFromBooking] = useState<Array<Order>>([])
  const [customerNumber, setCustomerNumber] = useState<string | undefined>()
  const [bookingStatus, setBookingStatus] = useState<Record<string, string>>({})
  const [bookingTriggered, setBookingTriggered] = useState<boolean>(false)
  const [bookingFinished, setBookingFinished] = useState<boolean>(false)
  const [inProgress, setInProgress] = useState<boolean>(false)
  const [currentlyDriving, setCurrentlyDriving] = useState(new Set<string>())
  const [labelFailure, setLabelFailure] = useState(false)
  const [waybillsFailure, setWaybillsFailure] = useState(false)
  const [labelsInProgress, setLabelsInProgress] = useState(false)
  const [waybillsInProgress, setWaybillsInProgress] = useState(false)
  const [returnLabelsInProgress, setReturnLabelsInProgress] = useState(false)
  const [bookingComplete, setBookingComplete] = useState<Array<string>>([])
  const [availableServices, setAvailableServices] = useState<
    Array<ServiceDetails>
  >([])
  const { __ } = useI18n()

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

  const fetchOrders = async (params: string) => {
    const response: Order[] = await apiFetch({
      path: '/posten-bring-checkout/bookings' + (params ? '?' + params : ''),
    })
    if (response) setOrders(response)
  }

  const fetchAvailableServices = async () => {
    const response: ServiceDetails[] = await apiFetch({
      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
    }

    setBookingTriggered(true)
    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 {
            await apiFetch({
              path: '/posten-bring-checkout/bookings?orderId=' + order.order_id,
              method: 'POST',
              headers: {
                'Content-Type': 'application/json',
              },
              body: JSON.stringify(data),
            })
            newBookingStatus[order.order_id] = 'success'
          } catch (error) {
            const bookingErrorMessage =
              error.message || __('Unknown error', 'posten-bring-checkout')

            const updatedOrder = {
              ...order,
              order_validation_errors: [
                ...order.order_validation_errors,
                { bookingDisabled: true, message: bookingErrorMessage },
              ],
            }

            setFailedFromBooking(prevFailed => [...prevFailed, updatedOrder])
            newBookingStatus[order.order_id] = 'error'
          }

          const newStatus = { ...newBookingStatus }
          setBookingStatus(newStatus)
          setBookingComplete(Object.keys(newStatus))
        }
      })

    await Promise.all(requests)
    setTimeout(() => {
      setBookingFinished(true)
      setInProgress(false)
    }, 400)
  }

  useEffect(() => {
    const done = new Set(bookingComplete)
    setTimeout(() => {
      setCurrentlyDriving(done)
    }, 50)
  }, [bookingComplete, bookingComplete.length])

  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 validationErrorsList = orders.filter(
    order => order.order_validation_errors.length > 0
  )

  const bookableOrders = orders.filter(
    order => (order.mybring_bookings?.length || 0) === 0
  )

  const bookedOrders = orders.filter(
    order =>
      (order.mybring_bookings?.length || 0) > 0 &&
      order.order_shipping_option &&
      order.order_shipping_option !== ''
  )

  const returnLabelsExist = !!orders.filter(
    order =>
      (order.mybring_bookings?.length || 0) > 0 &&
      order.mybring_bookings.filter(booking => booking.returnLabelUrl).length >
        0
  )

  const waybillsExist = !!orders.filter(
    order =>
      (order.mybring_bookings?.length || 0) > 0 &&
      order.mybring_bookings.filter(booking => booking.waybillUrl).length > 0
  )

  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
      )
      .map(order => order.order_id)
      .join(',')
    const response = await apiFetch<Response>({
      path: `/posten-bring-checkout/labels?orderIds=${orderIds}&labelType=${labelType}`,
      method: 'GET',
      headers: {
        Accept: 'application/pdf',
      },
      parse: false,
    }).catch(() => {
      setLabelFailure(true)
    })

    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 =>
          bookingStatus[order.order_id] === 'success' ||
          (order.mybring_bookings?.length || 0) > 0
      )
      .map(order => order.order_id)
      .join(',')
    const response = await apiFetch<Response>({
      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">
      <div className="header flex justify-csb align-ic flex-dir-row">
        <h1>
          {__('Book shipping for multiple orders', 'posten-bring-checkout')}
        </h1>
        <img className="bring-logo mrm" src={bringLogo} alt={''} />
      </div>
      <div>
        <div className="labels mtm flex justify-ce">
          {(bookableOrders.length === 0 && bookedOrders.length > 0) ||
          (bookingFinished && bookedOrders.length > 0) ||
          (bookingFinished &&
            bookableOrders.length > 0 &&
            Object.values(bookingStatus).includes('success')) ? (
            <div className="flex flex-dir-col gam">
              <ButtonGroup className="flex flex-dir-row gam flex-wrap">
                <Button
                  variant="secondary"
                  icon={arrowRight}
                  isBusy={labelsInProgress}
                  onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
                    e.preventDefault()
                    setLabelsInProgress(true)
                    getLabels('LABEL')
                  }}
                >
                  {__('Print labels', 'posten-bring-checkout')}
                </Button>
                {waybillsExist && (
                  <Button
                    variant="secondary"
                    icon={pages}
                    isBusy={waybillsInProgress}
                    onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
                      e.preventDefault()
                      setWaybillsInProgress(true)
                      getWaybills()
                    }}
                  >
                    {__('Print waybills', 'posten-bring-checkout')}
                  </Button>
                )}
                {returnLabelsExist && (
                  <Button
                    variant="secondary"
                    icon={undo}
                    isBusy={returnLabelsInProgress}
                    onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
                      e.preventDefault()
                      setReturnLabelsInProgress(true)
                      getLabels('RETURN')
                    }}
                  >
                    {__('Print return labels', 'posten-bring-checkout')}
                  </Button>
                )}
              </ButtonGroup>
              {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>
          ) : null}
        </div>
      </div>
      <div className="main-content flex flex-dir-col justify-csb">
        <div className="flex flex-dir-row justify-csb">
          <div className="mainbar">
            <div className="scrollable-container mbm">
              {orders && orders.length > 0 ? (
                <table className="order-list">
                  <tbody>
                    <tr>
                      <th>{__('Order id', 'posten-bring-checkout')}</th>
                      <th>{__('Shipping with', 'posten-bring-checkout')}</th>
                      <th>{__('Weight', 'posten-bring-checkout')}</th>
                      {(bookingTriggered ||
                        (bookableOrders.length === 0 &&
                          bookedOrders.length > 0)) && (
                        <th>{__('Status', 'posten-bring-checkout')}</th>
                      )}
                    </tr>
                    <Orders
                      orders={bookableOrders}
                      customerNumber={customerNumber}
                      customers={customers}
                      mapServiceId={mapServiceId}
                      bookingStatus={bookingStatus}
                      driving={currentlyDriving}
                      onSave={onSave}
                    />
                    <BookedOrders
                      orders={bookedOrders}
                      startIndex={bookableOrders.length}
                      mapServiceId={mapServiceId}
                    />
                  </tbody>
                </table>
              ) : (
                <div className="loading-spinner" data-testid="loading-spinner">
                  <Spinner
                    style={{
                      height: '40px',
                      width: '40px',
                    }}
                  ></Spinner>
                </div>
              )}
            </div>
            <div className="mbm">
              {validationErrorsList.length > 0 ||
              Object.values(bookingStatus).includes('error') ? (
                <div className="mtl">
                  <hr />
                  <b>
                    <span className="warning-icon">⚠</span>{' '}
                    {__(
                      'There are problems with some of the orders:',
                      'posten-bring-checkout'
                    )}
                  </b>
                  <div className="scrollable-container mtm">
                    <ul>
                      {failedFromBooking.map(order => (
                        <li key={order.order_id} className="error-list">
                          <strong>#{order.order_id}</strong> :{' '}
                          {order.order_validation_errors
                            .map(error => error.message)
                            .join(', ')}
                        </li>
                      ))}
                      {validationErrorsList.map(order =>
                        order.order_validation_errors.map(
                          (error: ValidationError) => (
                            <li key={order.order_id}>
                              <strong>#{order.order_id}</strong> :{' '}
                              {error.message}
                            </li>
                          )
                        )
                      )}
                    </ul>
                  </div>
                </div>
              ) : (
                ''
              )}
            </div>
          </div>
          <div className="flex justify-ce align-ife">
            <img
              className="collect-packages-img"
              src={collectPackagesImg}
              alt={''}
            />
          </div>
        </div>
        <div className="main-content__footer flex flex-dir-col justify-ce align-ife">
          <div className={'booking-form-wrapper'}>
            <div
              className={`booking-form ${
                bookingFinished || bookableOrders.length === 0 ? 'hide' : 'show'
              }`}
            >
              <CustomerSelector
                customers={customers}
                selectedCustomer={customerNumber}
                onSelect={selectedCustomerNumber =>
                  setCustomerNumber(selectedCustomerNumber)
                }
              />
              <div className="mts">
                <span className="mrs">
                  {/* eslint-disable-next-line @wordpress/i18n-no-flanking-whitespace */}
                  {__('By ordering, you accept our ', 'posten-bring-checkout')}
                  <a
                    href="https://www.bring.com/services/prices-and-conditions"
                    target="_blank"
                    rel="noreferrer"
                  >
                    {__('terms and conditions.', 'posten-bring-checkout')}
                  </a>
                </span>
                <Button
                  variant={'primary'}
                  className={'mtxs'}
                  data-testid="book-transport"
                  onClick={handleBooking}
                  isBusy={inProgress}
                  disabled={
                    !customerNumber ||
                    bookableOrders.length === 0 ||
                    (bookedOrders.length > 0 && bookableOrders.length === 0)
                  }
                >
                  {__('Book transport', 'posten-bring-checkout')}
                </Button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}

export default OrderList
