import { Card } from 'mystique/components/Card';
import { Children } from 'mystique/components/Children';
import { ListProps } from 'mystique/components/List';
import { Loading } from 'mystique/components/Loading';
import { pushToast } from 'mystique/frame/pushToast';
import { useI18n } from 'mystique/hooks/useI18n';
import { useQuery } from 'mystique/hooks/useQuery';
import {
  ApiSetting,
  SettingError,
  useSettings,
} from 'mystique/hooks/useSettings';
import { ComponentRegistry } from 'mystique/registry/ComponentRegistry';
import { FormFieldProps } from 'mystique/registry/FieldRegistry';
import { Connection, Edge, OkQueryResponse } from 'mystique/types/common';
import { MystiqueComponentInstance } from 'mystique/types/manifest';
import {
  FC,
  Reducer,
  useCallback,
  useEffect,
  useMemo,
  useReducer,
  useState,
} from 'react';
import {
  BarcodeFilterProps,
  QuantitySelectorEvent,
  QuantitySelectorValidation,
  TranslatableAttribute,
} from 'mystique/components/QuantityList';
import { recursiveValueDecorator } from 'mystique/utils';
import _ from 'lodash';
import { UserAttribute } from 'mystique/hooks/useAuth';
import { PhotoCaptureEvent } from '../../../components/PhotoCapture/PhotoCapture';
import { eqIgnoreCase } from '../../../utils/StringUtils';
import { InnerText } from 'mystique/components/QuantitySelectorComponent';

export interface ReturnItemState extends ReturnItemAttributes {
  isSelected: boolean;
  returnableQty: number;
  nonReturnableReason?: string;
  nonReturnableReasonFallback?: string;
}

interface SettingOption {
  label: string;
  value: string;
}

export interface ReturnItemAttributes {
  orderItemRef?: string;
  returnCondition?: SettingOption;
  returnReason?: SettingOption;
  photo?: string;
  attributes?: UserAttribute[];
  returnReasonComment?: string;
  unitQuantity: {
    quantity: number;
  };
}

export type ReturnReducerState = {
  returnItems: ReturnItemState[];
  globalStateValid?: boolean;
  dataValid: boolean | null;
  init: boolean;
};

export type QuantitySelectorProps = FormFieldProps<QuantitySelectorEvent> & {
  /**
   * The validation settings for the quantity selector
   */
  validation?: QuantitySelectorValidation;
  /**
   * The data source for the quantity selector. This works the same way as a Mystique Component Instance's dataSource property.
   */
  dataSource?: string;
  /**
   * An optional feature where you define whether the data passed into the quantity selector is valid or not. <br/>
   * With this feature, you can instead display a string instead of the component when the data is not valid for a quantity selector. <br/>
   *
   * This is separate from the validation prop which is used for validating user input only.
   */
  itemValidation?: {
    /**
     * The condition to check whether or not the item is valid or not. This should be a templated string that resolves to a boolean.
     * For example, if you want to check that the amount of quantity is greater than zero before showing the quantity selector you can
     * use {{gt orderItem.quantity 0}}.
     */
    condition: string;
    /**
     * If the data for this quantity selector is invalid according to isValid, this message will be displayed instead of the
     * quantity selector component. This can be an i18n string. If the key is not found, or this is not provided, then the component defaults
     * to using the error 'Invalid Item'
     */
    messageOnInvalid?: string;
  };

  /**
   * An optional bit of text that's shown inside the quantity selector. If this isn't provided, the text will not be shown.
   */
  innerText?: InnerText;
  /**
   * The data provided by parent data providers
   */
  data?: any;
};

/**
 * The properties of the quantity list. It contains all properties a list usually has, plus it accepts
 * properties for the quantity selector as well. The attribute label also has a fallback value if the original
 * label is an i18n key and it does not resolve to a string. (IE a translation was not found for that key.)
 */
interface QuantityListProps extends ListProps {
  /**
   * The quantity selector props
   */
  quantitySelectorProps: QuantitySelectorProps;
  /**
   * Attributes where labels are translatable
   */
  attributes: TranslatableAttribute[];
  /**
   * Props passed to the barcode filter
   */
  scannerProps?: BarcodeFilterProps;
}

export type ReturnReducerAction = InitAction | UpdateAction;

interface InitAction {
  type: 'init';
  value: OrderByIdResponse;
  useFilledQuantity: boolean;
}

interface UpdateAction {
  type: 'update';
  value: ReturnItemState;
}

export interface OrderByIdResponse {
  orderById: Items;
}

interface Items {
  id: string;
  ref?: string;
  status?: string;
  type: string;
  items?: Connection<OrderItem>;
  fulfilments?: Connection<Fulfilment>;
}

export interface Fulfilment {
  id: string;
  items?: Connection<FulfilmentItem>;
}

interface FulfilmentItem {
  id: string;
  filledQuantity: number;
  orderItem: OrderItemLink;
}

interface OrderItemLink {
  id: string;
}

type HighlightColour = 'warning' | 'success' | 'error';

export interface OrderItem {
  id: string;
  ref?: string;
  quantity: number;
  status: string;
  attributes?: Attribute[] | null;
  totalPrice?: number;
  totalTaxPrice?: number;
  paidPrice?: number;
  price?: number;
  taxPrice?: number;
  currency?: string;
  highlight?: {
    colour?: HighlightColour;
  };
  product: {
    id: string;
    name: string;
    ref: string;
    status?: string;
    attributes?: Attribute[];
    categories?: Connection<Category>;
    prices?: Price[];
    gtin?: string;
  };
}

export interface OrderItemPlusState extends OrderItem {
  state: ReturnItemState;
}

export interface Attribute {
  name: string;
  value: unknown;
}

interface Category {
  name: string;
}

interface Price {
  type: string;
  value: number;
  currency: string;
}

export interface ReturnsFieldConfig
  extends Omit<ListProps, 'data' | 'defaultPageSize' | 'attributes'> {
  query?: string;
  defaultPageSize?: number;
  summary?: {
    descendants?: MystiqueComponentInstance[];
  };
  attributes?: TranslatableAttribute[];
  scanner?: {
    scannerFields?: string[];
    filterFields?: string[];
    rowId?: string;
    scanTimeout?: number;
    incrementBy?: number;
    toastTimeout?: number;
    highlightDuration?: number;
  };
  useFilledQuantity?: boolean;
}

const orderQuery = `
query orderById($orderId: ID!) {
    orderById(id: $orderId) {
      id
      ref
      status
      type
      createdOn
      updatedOn
      customer {
        ref
        title
        firstName
        lastName
        primaryPhone
        primaryEmail
        username
      }
      totalPrice
      totalTaxPrice
      items(first: 500) {
        edges {
          node {
            id
            ref
            quantity
            status
            totalPrice
            totalTaxPrice
            taxPrice
            price
            paidPrice
            currency
            attributes {
              name
              value
            }
            product {
              name
              ... on StandardProduct {
                id
                ref
                status
                prices {
                  type
                  value
                  currency
                }
                attributes {
                  name
                  value
                }
                categories(first: 1) {
                  edges {
                    node {
                      name
                    }
                  }
                }
              }
              ... on GroupProduct {
                id
                ref
                status
                prices {
                  type
                  value
                  currency
                }
                attributes {
                  name
                  value
                }
                categories(first: 1) {
                  edges {
                    node {
                      name
                    }
                  }
                }
              }
              ... on VariantProduct {
                id
                ref
                status
                gtin
                prices {
                  type
                  value
                  currency
                }
                attributes {
                  name
                  value
                }
                categories(first: 1) {
                  edges {
                    node {
                      name
                    }
                  }
                }
              }
            }
          }
        }
      }
      fulfilments (first:50) {
        edges{
          node{
            items (first:100) {
              edges{
                node {
                  id
                  filledQuantity
                  orderItem {
                    id
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  `;

const isGlobalStateValid = (arr: ReturnItemState[] | undefined) => {
  const totalQuantity = arr
    ?.filter((item) => item?.returnableQty > 0)
    .reduce((sum, r) => sum + r.unitQuantity.quantity, 0);
  if (arr?.length === 0 || !totalQuantity) return false;

  return totalQuantity > 0;
};

const isDataValid = (data: Partial<OrderByIdResponse>): boolean => {
  if (!data.orderById) {
    console.error(
      'Returns data error: Missing orderById query. Refer to returns docs for a complete query.',
    );
    return false;
  }
  if (!data.orderById.items || !data.orderById.items.edges) {
    console.error(
      'Returns data error: Missing orderById.items. Refer to returns docs for a complete query.',
    );
    return false;
  }
  for (const item of data.orderById.items.edges) {
    if (!item.node.ref) {
      console.error(
        `Returns data error: Order id ${item.node.id} is missing ref. Refer to returns docs for a complete query.`,
      );
      return false;
    }
    if (item.node.quantity === undefined) {
      console.error(
        `Returns data error: Order ${item.node.ref} is missing quantity. Refer to returns docs for a complete query.`,
      );
      return false;
    }
  }
  return true;
};

export const getFilledQuantity = (
  orderItemId: string,
  fulfilments: Connection<Fulfilment> | undefined,
): number | undefined => {
  if (!fulfilments) {
    return undefined;
  }
  return fulfilments.edges
    .flatMap((f) => f.node.items?.edges.map((fi) => fi.node) || [])
    .filter((fi) => fi?.orderItem.id === orderItemId)
    .map((fi) => fi?.filledQuantity || 0)
    .reduce((sum, current) => sum + current, 0);
};

const reducer: Reducer<ReturnReducerState, ReturnReducerAction> = (
  state: ReturnReducerState,
  action: ReturnReducerAction,
) => {
  const newState = { ...state };

  switch (action.type) {
    case 'init': {
      newState.globalStateValid = false;
      newState.dataValid = false;
      if (!action.value) return newState;
      if (!isDataValid(action.value)) return newState;

      newState.dataValid = true;
      const initialReturnItems: ReturnItemState[] = [];
      const orderItems = action.value.orderById.items?.edges || [];
      for (const orderItem of orderItems) {
        const returnItem: ReturnItemState = {
          isSelected: false,
          orderItemRef: orderItem.node.ref,
          unitQuantity: {
            quantity: 0,
          },
          returnableQty:
            (orderItem.node.attributes?.find((a) => a.name === 'returnableQty')
              ?.value as number) ??
            (action.useFilledQuantity
              ? getFilledQuantity(
                orderItem.node.id,
                action.value.orderById.fulfilments,
              )
              : undefined) ??
            orderItem.node.quantity ??
            0,
          nonReturnableReason:
            'fc.sf.ui.returns.orders.createReturnOrder.list.returnQuantitySelector.allItemsReturned.error',
          nonReturnableReasonFallback: 'All items already returned',
        };

        const returnType = (
          orderItem.node.attributes?.find((a) => a.name === 'RETURN_TYPE')
            ?.value as string
        )
          ?.trim()
          .toUpperCase();
        const returnDateLimit = orderItem.node.attributes?.find(
          (a) => a.name === 'RETURN_DATE_LIMIT',
        )?.value as string;

        if (returnType && returnType !== 'RMA' && returnType !== 'DEFAULT') {
          returnItem.returnableQty = 0;
          returnItem.nonReturnableReason =
            'fc.sf.ui.returns.orders.createReturnOrder.list.returnQuantitySelector.nonReturnableItem.error';
          returnItem.nonReturnableReasonFallback = 'Non-returnable item';
        } else if (
          returnDateLimit &&
          Date.parse(returnDateLimit) < new Date().getTime()
        ) {
          returnItem.returnableQty = 0;
          returnItem.nonReturnableReason =
            'fc.sf.ui.returns.orders.createReturnOrder.list.returnQuantitySelector.timeExpired.error';
          returnItem.nonReturnableReasonFallback = 'Return time expired';
        }

        initialReturnItems.push(returnItem);
      }
      newState.returnItems = initialReturnItems;
      newState.init = true;
      newState.globalStateValid = isGlobalStateValid(initialReturnItems);

      return newState;
    }
    case 'update': {
      const returnItem = action.value;
      const selectedReturnItem = newState.returnItems?.find(
        (item) => returnItem.orderItemRef === item.orderItemRef,
      );

      if (selectedReturnItem) {
        selectedReturnItem.isSelected = returnItem.isSelected;
        selectedReturnItem.unitQuantity = returnItem.unitQuantity;
        selectedReturnItem.orderItemRef = returnItem.orderItemRef;
        selectedReturnItem.returnReasonComment = returnItem.returnReasonComment;
        selectedReturnItem.returnCondition = returnItem.returnCondition;
        selectedReturnItem.returnReason = returnItem.returnReason;
      }

      if (returnItem.photo && selectedReturnItem) {
        selectedReturnItem.attributes = [
          {
            name: 'returnImage',
            value: returnItem.photo,
            type: 'STRING',
          },
        ];
      }

      newState.globalStateValid = isGlobalStateValid(newState.returnItems);

      return newState;
    }
    default:
      throw new Error(
        'Unsupported operation when calling useReducer dispatch function',
      );
  }
};

export const defaultColumns: TranslatableAttribute[] = [
  // product image
  {
    label:
      'i18n:fc.sf.ui.returns.orders.createReturnOrder.list.column.product.label',
    labelFallback: 'Product',
    type: 'image',
    options: {
      height: 50,
    },
    value: '{{node.product.attributes.byName.imageUrl}}',
  },
  // product name
  {
    label: '',
    labelFallback: '',
    type: 'component',
    options: {
      component: 'fc.mystique.collapsible.text',
      props: {
        text: '{{{node.product.name}}}',
        charCutoff: 300,
      },
    },
  },
  // Description
  {
    label: 'i18n:fc.sf.ui.returns.orders.createReturnOrder.list.description',
    labelFallback: 'Description',
    type: 'component',
    align: 'center',
    options: {
      component: 'fc.se.button.show.attributes',
      dataSource: 'node',
      props: {
        title:
          "{{i18n 'fc.sf.ui.returns.orders.createReturnOrder.list.productAttributes.title'}}",
        fields: [
          {
            label:
              "{{i18n 'fc.sf.ui.returns.orders.createReturnOrder.list.productAttributes.ref.label'}}",
            value: '{{ref}}',
          },
        ],
        attributes: [
          {
            dataSource: 'product.attributes',
            exclude: ['imageUrl', 'imageUrlRef'],
          },
        ],
      },
    },
  },
  // Expire on
  {
    label: 'i18n:fc.sf.ui.returns.orders.createReturnOrder.list.expireOn',
    labelFallback: 'Expire on',
    value:
      "{{#if node.attributes.byName.RETURN_DATE_LIMIT}}{{dateStringFormatter node.attributes.byName.RETURN_DATE_LIMIT 'DD MMM YYYY'}}{{else}}-{{/if}}",
    align: 'center',
  },
  // Ordered Qty
  {
    label:
      'i18n:fc.sf.ui.returns.orders.createReturnOrder.list.column.orderedQty.label',
    value: '{{node.quantity}}',
    labelFallback: 'Ordered qty',
    align: 'center',
  },
  // Returnable Qty
  {
    label:
      'i18n:fc.sf.ui.returns.orders.createReturnOrder.list.column.returnableQty.label',
    value: '{{node.state.returnableQty}}',
    labelFallback: 'Returnable qty',
    align: 'center',
  },
  // quantity selector is always appended
];

const populateState = (
  data: OrderByIdResponse,
  state: ReturnReducerState,
): OrderByIdResponse => {
  data?.orderById.items?.edges.forEach((edge) => {
    const itemState = state.returnItems.find(
      (i) => i.orderItemRef === edge.node.ref,
    );
    if (itemState) {
      (edge.node as OrderItemPlusState).state = _.cloneDeep(itemState);
    }
  });
  return data;
};

const settingNotFound = (
  response: OkQueryResponse<ApiSetting> | SettingError,
): boolean => {
  return (
    response.status === 'error' &&
    response.resultType === 'string' &&
    response.result === 'Setting not found.'
  );
};

export const ReturnItemsExtended: FC<
  FormFieldProps<ReturnItemAttributes[] | null>
> = ({ onChange, entityContext, lastSubmit }) => {
  const { translateOr, prefixedTranslate } = useI18n();
  const settings = useSettings({
    conditions: 'RETURN_CONDITION',
    reasons: 'RETURN_REASON',
    fieldConfig: 'fc.mystique.fields.returns',
  });
  const returnFieldConfigRaw: ReturnsFieldConfig | undefined =
    settings.fieldConfig.status === 'ok'
      ? settings.fieldConfig.result.value
      : undefined;
  const returnFieldConfig = returnFieldConfigRaw
    ? (recursiveValueDecorator(
      returnFieldConfigRaw,
      'string',
      prefixedTranslate,
    ) as ReturnsFieldConfig)
    : undefined;
  const query = returnFieldConfig?.query || orderQuery;
  const [response] = useQuery<OrderByIdResponse>(query, {
    orderId: entityContext?.[0].entity.id || -1,
  });
  const { fetching, data } = response;
  const [state, dispatch] = useReducer<
    Reducer<ReturnReducerState, ReturnReducerAction>
  >(reducer, {
    init: false,
    returnItems: [],
    dataValid: null,
  });
  const columns = returnFieldConfig?.attributes || defaultColumns;
  const dataWithState =
    data && state.init ? populateState(data, state) : undefined;
  const [filterData, setFilterData] =
    useState<Connection<OrderItem> | undefined>();
  const [filterValue, setFilterValue] = useState<string | undefined>('');

  // Filter/scan props
  const filterFields = useMemo(
    () =>
      returnFieldConfig?.scanner?.filterFields ?? [
        'node.product.gtin',
        'node.product.name',
      ],
    [returnFieldConfig?.scanner?.filterFields],
  );
  const scannerFields = returnFieldConfig?.scanner?.scannerFields ?? [
    'node.product.gtin',
  ];
  const rowId = returnFieldConfig?.scanner?.rowId ?? 'node.product.gtin';
  const scanTimeout = returnFieldConfig?.scanner?.scanTimeout;
  const incrementBy = returnFieldConfig?.scanner?.incrementBy ?? 1;
  const toastTimeout = returnFieldConfig?.scanner?.toastTimeout ?? 2000;
  const highlightDuration =
    returnFieldConfig?.scanner?.highlightDuration ?? 300;

  const initSummary = {
    price: 0,
    paidPrice: 0,
    taxPrice: 0,
  };

  const priceSummary =
    dataWithState?.orderById.items?.edges.reduce((prev, _cur: unknown) => {
      const cur = _cur as Edge<OrderItemPlusState>;
      prev.price =
        prev.price +
        cur.node.state.unitQuantity.quantity * (cur.node.price || 0);
      prev.paidPrice =
        prev.paidPrice +
        cur.node.state.unitQuantity.quantity * (cur.node.paidPrice || 0);
      prev.taxPrice =
        prev.taxPrice +
        cur.node.state.unitQuantity.quantity * (cur.node.taxPrice || 0);
      return prev;
    }, initSummary) ?? initSummary;

  const handleChange = (event: QuantitySelectorEvent) => {
    const returnItem = state.returnItems.find(
      (i) => i.orderItemRef === event.id,
    );
    if (!returnItem) {
      console.error(
        'Error handling quantity selector change: Could not find item with ref ' +
          event.id,
      );
      return;
    }
    const count = event.value;
    const returnableQuantity = returnItem.returnableQty;
    const returnItemUpdate = { ...returnItem };
    returnItemUpdate.isSelected = count > 0 && count <= returnableQuantity;
    returnItemUpdate.unitQuantity.quantity = Number(count);

    handleFilter('');
    setFilterValue('');
    dispatch({ type: 'update', value: returnItemUpdate });
  };

  const handleImgChange = (event: PhotoCaptureEvent) => {
    const returnItem = state.returnItems.find(
      (i) => i.orderItemRef === event.id,
    );
    if (!returnItem) {
      console.error(
        'Error handling quantity selector change: Could not find item with ref ' +
          event.id,
      );
      return;
    }
    const returnItemUpdate = { ...returnItem };
    returnItemUpdate.photo = event.value;

    dispatch({ type: 'update', value: returnItemUpdate });
  };

  const expansionProps: MystiqueComponentInstance = {
    component: 'fc.return.rowExpansion',
    props: {
      settings: settings,
      dispatch: dispatch,
      colSpan: columns.length + 1,
    },
    dataSource: 'node',
  };

  const imageCaptureAttr: TranslatableAttribute = {
    type: 'component',
    label:
      'i18n:fc.sf.ui.returns.orders.createReturnOrder.list.returnItem.imageCapture',
    labelFallback: 'Capture',
    options: {
      component: 'fc.se.photo.capture',
      props: {
        id: '{{node.state.orderItemRef}}',
        onChange: handleImgChange,
        data: dataWithState?.orderById.items,
      },
    },
    align: 'center',
  };

  const receiptAttr: TranslatableAttribute = {
    label:
      'i18n:fc.sf.ui.returns.orders.createReturnOrder.list.returnItem.isReceipt',
    labelFallback: 'Is Receipt',
    options: {
      styles: [
        {
          value: "{{eq node.product.attributes.byName.RETURN_TYPE 'RMA'}}",
          matches: ['true'],
          icon: {
            name: 'MdCheckCircleOutline',
            colour: 'green',
          },
        },
        {
          icon: {
            name: 'MdOutlineHighlightOff',
            colour: 'lightgray',
          },
        },
      ],
    },
    align: 'center',
  };

  const priceAttr: TranslatableAttribute = {
    label:
      'i18n:fc.sf.ui.returns.orders.createReturnOrder.list.returnItem.price',
    labelFallback: 'Net Price',
    value:
      '{{currency node.product.prices.0.value node.product.prices.0.currency}}',
    align: 'center',
  };

  let modifiedAttributes = [
    ...columns,
    priceAttr,
    imageCaptureAttr,
    receiptAttr,
  ];
  for (const attr of modifiedAttributes) {
    attr.label = prefixedTranslate([
      attr.label,
      attr.labelFallback || attr.label,
    ]);
  }
  modifiedAttributes = recursiveValueDecorator(
    modifiedAttributes,
    'string',
    prefixedTranslate,
  );

  const listProps = {
    ...returnFieldConfig,
    row: returnFieldConfig?.row || { expansion: expansionProps },
    defaultPageSize: returnFieldConfig?.defaultPageSize || 100,
    attributes: modifiedAttributes,
    data: filterData ?? dataWithState?.orderById.items,
    responsiveness: returnFieldConfig?.responsiveness || 'card',
    rowId,
    density: {
      toggle: false,
      initial: 'compressed',
    },
  } as ListProps;

  const highlightRow = useCallback(
    (id: string, colour: HighlightColour) => {
      const newData = dataWithState?.orderById.items?.edges.map((item) =>
        item.node.id === id
          ? {
            ...item,
            node: {
              ...item.node,
              highlight: { colour, fadeDuration: highlightDuration },
            },
          }
          : item,
      );

      if (newData?.length) {
        setFilterData({ edges: newData });
      }
    },
    [dataWithState?.orderById.items?.edges],
  );

  const handleFilter = useCallback(
    (value: string) => {
      if (!value) {
        setFilterData(undefined);
        return;
      }
      const newItems = dataWithState?.orderById.items?.edges.filter((item) =>
        filterFields.some((filterField) =>
          String(_.get(item, filterField))
            ?.toLowerCase()
            .includes(value.toLowerCase()),
        ),
      );

      if (newItems !== undefined) {
        setFilterData({ edges: newItems });
      }
    },
    [dataWithState?.orderById.items, filterFields],
  );

  const handleScan = useCallback(
    (value: string) => {
      const matchItems = dataWithState?.orderById.items?.edges.filter((item) =>
        scannerFields.some(
          (filterField) =>
            String(_.get(item, filterField))?.toLowerCase() ===
            value.toLowerCase(),
        ),
      );

      if (!matchItems) return;

      if (matchItems.length <= 0) {
        handleFilter(value);
        setFilterValue(value);
        // Item not found
        pushToast(
          'warning',
          translateOr([
            'fc.sf.ui.returns.scan.toast.none',
            'Scanned item not found',
          ]),
          toastTimeout,
        );
      } else if (matchItems.length === 1) {
        const { state, ref, id } = matchItems[0].node as OrderItemPlusState;

        if (state.returnableQty <= 0) {
          // Item non returnable
          pushToast(
            'warning',
            translateOr([
              'fc.sf.ui.returns.scan.toast.unreturnable',
              'Scanned item is non-returnable',
            ]),
            toastTimeout,
          );
          highlightRow(id, 'warning');
          setFilterValue('');
        } else if (state.returnableQty === state.unitQuantity.quantity) {
          // Item at max returnable quantity
          pushToast(
            'warning',
            translateOr([
              'fc.sf.ui.returns.scan.toast.atMax',
              'Max item quantity already reached',
            ]),
            toastTimeout,
          );
          highlightRow(id, 'warning');
          setFilterValue('');
        } else {
          // Increment item return quantity
          handleChange({
            id: ref ?? '',
            value: state.unitQuantity.quantity + incrementBy,
          });
          highlightRow(id, 'success');
        }

        const targetRowId = _.get(matchItems[0], rowId);
        const item = document.querySelector(`#${CSS.escape(targetRowId)}`);
        if (item) {
          item.scrollIntoView({ behavior: 'auto', block: 'center' });
        }
      } else if (matchItems.length > 1) {
        pushToast(
          'warning',
          translateOr([
            'fc.sf.ui.returns.scan.toast.multiple',
            'More than one item was found',
          ]),
          toastTimeout,
        );
        handleFilter(value);
        setFilterValue(value);
      }
    },
    [
      dataWithState?.orderById.items?.edges,
      handleChange,
      highlightRow,
      incrementBy,
      scannerFields,
      toastTimeout,
      translateOr,
    ],
  );

  const BarcodeProps: BarcodeFilterProps = {
    onScan: handleScan,
    onFilter: handleFilter,
    setValue: setFilterValue,
    value: filterValue,
    currentCount:
      filterData?.edges.length ?? dataWithState?.orderById.items?.edges.length,
    totalCount: dataWithState?.orderById.items?.edges.length,
    debounceTimeout: scanTimeout,
  };

  const summaryContent = () => {
    if (returnFieldConfig?.summary?.descendants?.length) {
      return (
        <Children
          descendantsOverride={returnFieldConfig.summary.descendants}
          dataOverride={{ ...dataWithState, priceSummary }}
        />
      );
    }
    return undefined;
  };

  useEffect(() => {
    if (!data || fetching) return;
    if (!state.init) {
      dispatch({
        type: 'init',
        value: data,
        useFilledQuantity: returnFieldConfig?.useFilledQuantity || false,
      });
    }
  }, [JSON.stringify(data), fetching]);

  useEffect(() => {
    window.sessionStorage.setItem('HIDE_DESTINATION_LOCATION', 'true');
    window.sessionStorage.setItem('HIDE_PICKUP_LOCATION', 'true');
    window.sessionStorage.setItem('RETURNS_AMOUNT', '0.00');
    window.sessionStorage.setItem('RETURNS_CURRENCY', 'USD');
  }, []);

  useEffect(() => {
    if (lastSubmit) {
      if (!state.globalStateValid) {
        pushToast(
          'error',
          translateOr([
            'fc.sf.ui.returns.orders.createReturnOrder.error.noItemSelected',
            'Require at least 1 selected return qty',
          ]),
          2000,
        );
      }
    }
  }, [lastSubmit]);

  useEffect(() => {
    if (state.dataValid == false) {
      pushToast(
        'error',
        translateOr([
          'fc.sf.ui.returns.error.configurationError',
          'Configuration error. Contact IT Support.',
        ]),
        8000,
      );
    }
  }, [state.dataValid]);

  useEffect(() => {
    let hideDestinationLocation = true;
    let hidePickupLocation = true;
    let returnsAmount = 0;
    if (state.globalStateValid) {
      const payload: ReturnItemAttributes[] = [];

      state.returnItems?.map((item) => {
        if (item.isSelected) {
          const selectedReturnItem: ReturnItemAttributes = {
            orderItemRef: item.orderItemRef,
            unitQuantity: item.unitQuantity,
            returnCondition: item.returnCondition,
            returnReason: item.returnReason,
            returnReasonComment: item.returnReasonComment,
            attributes: item.attributes,
          };

          payload.push(selectedReturnItem);
          if (
            selectedReturnItem.returnCondition &&
            selectedReturnItem.returnCondition.value === 'GOOD'
          ) {
            hideDestinationLocation = false;
          }
          dataWithState?.orderById?.items?.edges.forEach((value) => {
            if (item.orderItemRef === value.node?.product.ref) {
              returnsAmount += value.node.product?.prices
                ? value.node.product.prices[0].value *
                  item.unitQuantity.quantity
                : 0.0;
              value.node.product?.attributes?.forEach((att) => {
                if (att.name === 'RETURN_TYPE' && att.value === 'RMA') {
                  hidePickupLocation = false;
                }
              });
            }
          });
        }
      });

      onChange(payload, { summary: { content: summaryContent() } });
    } else {
      onChange(null, { summary: { content: summaryContent() } });
    }
    window.sessionStorage.setItem('RETURNS_AMOUNT', String(returnsAmount));
    window.sessionStorage.setItem(
      'RETURNS_CURRENCY',
      dataWithState?.orderById?.items?.edges[0].node.currency || 'USD',
    );
    window.sessionStorage.setItem(
      'HIDE_DESTINATION_LOCATION',
      String(hideDestinationLocation),
    );
    window.sessionStorage.setItem(
      'HIDE_PICKUP_LOCATION',
      String(hidePickupLocation),
    );
  }, [
    JSON.stringify(state.returnItems),
    state.globalStateValid,
    returnFieldConfigRaw,
  ]);

  useEffect(() => {
    //This effect basically validates if the return reason and conditions are valid, and makes toasts if we find anything wrong.
    if (
      settings.conditions.status === 'loading' ||
      settings.reasons.status === 'loading'
    ) {
      //If settings are still loading, wait for them to load
      return;
    }
    //Validate conditions
    if (settings.conditions.status === 'ok') {
      const conditionOptions = settings.conditions.result
        ?.value as SettingOption[];
      if (conditionOptions && !Array.isArray(conditionOptions)) {
        pushToast(
          'error',
          translateOr([
            'fc.sf.ui.returns.error.malformedConditionSettings',
            'Malformed settings in return condition. Contact IT Support.',
          ]),
          8000,
        );
        console.error(
          'The RETURN_CONDITION setting could not be parsed as an array of value, label pairs',
        );
      }
    }
    //Validate reasons
    if (settings.reasons.status === 'ok') {
      const reasonOptions = settings.reasons.result?.value as SettingOption[];
      if (!Array.isArray(reasonOptions)) {
        pushToast(
          'error',
          translateOr([
            'fc.sf.ui.returns.error.malformedReasonSettings',
            'Malformed settings in return reason. Contact IT Support.',
          ]),
          8000,
        );
        console.error(
          'The RETURN_REASON setting could not be parsed as an array of value, label pairs',
        );
      }
    }
    //Check for errors.
    if (
      settingNotFound(settings.conditions) &&
      settingNotFound(settings.reasons)
    ) {
      pushToast(
        'error',
        translateOr([
          'fc.sf.ui.returns.error.missingConditionReasonSettings',
          'Missing condition / reason setting. Contact IT Support.',
        ]),
        8000,
      );
    } else if (settingNotFound(settings.conditions)) {
      pushToast(
        'error',
        translateOr([
          'fc.sf.ui.returns.error.missingConditionSettings',
          'Missing condition setting. Contact IT Support.',
        ]),
        8000,
      );
    } else if (settingNotFound(settings.reasons)) {
      pushToast(
        'error',
        translateOr([
          'fc.sf.ui.returns.error.missingReasonSettings',
          'Missing reason setting. Contact IT Support.',
        ]),
        8000,
      );
    }
  }, [settings.conditions.status, settings.reasons.status]);

  if (eqIgnoreCase(entityContext?.[0]?.entity.type, 'order')) {
    if (
      state.init != false &&
      settings.fieldConfig.status !== 'loading' &&
      dataWithState &&
      state.dataValid
    ) {
      const fieldProps: QuantitySelectorProps = {
        onChange: handleChange,
        name: '{{productName}}',
        validation: { min: 0, max: '{{returnableQty}}' },
        label: '',
        dataSource: 'node.state',
        value: { value: '{{unitQuantity.quantity}}', id: '{{orderItemRef}}' },
        itemValidation: {
          condition: '{{gt returnableQty 0}}',
          messageOnInvalid:
            "{{i18n '' nonReturnableReason _fallback=nonReturnableReasonFallback}}",
        },
        onBlur: () => null,
        innerText: {
          string: 'i18n:fc.sf.ui.returns.orders.createReturnOrder.count.ofMax',
          options: {
            color: {
              all: 'success',
              some: 'warning',
              none: 'error',
            },
          },
        },
      };
      const QuantityList = ComponentRegistry.get(
        'fc.se.quantity.list',
      ) as FC<QuantityListProps>;
      return (
        <QuantityList
          {...(listProps as QuantityListProps)}
          quantitySelectorProps={fieldProps}
          scannerProps={BarcodeProps}
        />
      );
    } else if (fetching || settings.fieldConfig.status === 'loading') {
      return (
        <div style={{ minHeight: '4em' }}>
          <Loading />
        </div>
      );
    } else if (state.dataValid == false) {
      return (
        <Card width={12}>
          <p style={{ textAlign: 'center' }}>
            {translateOr([
              'fc.sf.ui.returns.orders.createReturnOrder.error.dataInvalidMessage',
              'Unable to display data due to configuration error',
            ])}
          </p>
        </Card>
      );
    } else {
      return (
        <Card width={12}>
          <p style={{ textAlign: 'center' }}>
            {translateOr([
              'fc.sf.ui.returns.orders.createReturnOrder.error.noDataMessage',
              'No data available to render component',
            ])}
          </p>
        </Card>
      );
    }
  } else {
    return (
      <Card width={12}>
        <p style={{ textAlign: 'center' }}>
          {translateOr([
            'fc.sf.ui.returns.orders.createReturnOrder.error.orderUserActions',
            'This field is only applicable to Order user actions',
          ])}
        </p>
      </Card>
    );
  }
};
