import StyledWrapper from "@insite/client-framework/Common/StyledWrapper";
import { SafeDictionary } from "@insite/client-framework/Common/Types";
import getFileExtension from "@insite/client-framework/Common/Utilities/getFileExtension";
import parseQueryString from "@insite/client-framework/Common/Utilities/parseQueryString";
import siteMessage from "@insite/client-framework/SiteMessage";
import ApplicationState from "@insite/client-framework/Store/ApplicationState";
import batchLoadProducts, {
    UploadError,
} from "@insite/client-framework/Store/Components/OrderUpload/Handlers/BatchLoadProducts";
import cleanupUploadData from "@insite/client-framework/Store/Components/OrderUpload/Handlers/CleanupUploadData";
import { OrderUploadRowError } from "@insite/client-framework/Store/Components/OrderUpload/OrderUploadState";
import processFile from "@insite/client-framework/Store/Components/VmiBinsImportModal/Handlers/ProcessFile";
import setIsBadFile from "@insite/client-framework/Store/Components/VmiBinsImportModal/Handlers/SetIsBadFile";
import setIsUploading from "@insite/client-framework/Store/Components/VmiBinsImportModal/Handlers/SetIsUploading";
import setUploadLimitExceeded from "@insite/client-framework/Store/Components/VmiBinsImportModal/Handlers/SetUploadLimitExceeded";
import setVmiBinsImportModalIsOpen from "@insite/client-framework/Store/Components/VmiBinsImportModal/Handlers/SetVmiBinsImportModalIsOpen";
import { getLocation } from "@insite/client-framework/Store/Data/Pages/PageSelectors";
import addVmiBins from "@insite/client-framework/Store/Data/VmiBins/Handlers/AddVmiBins";
import loadVmiBins from "@insite/client-framework/Store/Data/VmiBins/Handlers/LoadVmiBins";
import { getVmiBinsDataView } from "@insite/client-framework/Store/Data/VmiBins/VmiBinsSelectors";
import { VmiLocationStateContext } from "@insite/client-framework/Store/Data/VmiLocations/VmiLocationsSelectors";
import translate from "@insite/client-framework/Translate";
import { VmiBinModel } from "@insite/client-framework/Types/ApiModels";
import Button, { ButtonPresentationProps } from "@insite/mobius/Button";
import Checkbox, { CheckboxPresentationProps, CheckboxProps } from "@insite/mobius/Checkbox";
import DataTable, { DataTableProps } from "@insite/mobius/DataTable";
import DataTableBody, { DataTableBodyProps } from "@insite/mobius/DataTable/DataTableBody";
import DataTableCell from "@insite/mobius/DataTable/DataTableCell";
import { DataTableCellBaseProps } from "@insite/mobius/DataTable/DataTableCellBase";
import DataTableHead, { DataTableHeadProps } from "@insite/mobius/DataTable/DataTableHead";
import DataTableHeader, { DataTableHeaderProps } from "@insite/mobius/DataTable/DataTableHeader";
import DataTableRow, { DataTableRowProps } from "@insite/mobius/DataTable/DataTableRow";
import FileUpload, { FileUploadPresentationProps } from "@insite/mobius/FileUpload";
import GridContainer, { GridContainerProps } from "@insite/mobius/GridContainer";
import GridItem, { GridItemProps } from "@insite/mobius/GridItem";
import Link, { LinkPresentationProps } from "@insite/mobius/Link";
import Modal, { ModalPresentationProps } from "@insite/mobius/Modal";
import { HasToasterContext, withToaster } from "@insite/mobius/Toast/ToasterContext";
import Typography, { TypographyPresentationProps } from "@insite/mobius/Typography";
import InjectableCss from "@insite/mobius/utilities/InjectableCss";
import React, { useContext, useEffect, useRef, useState } from "react";
import { connect, ResolveThunks } from "react-redux";
import { css } from "styled-components";

const mapStateToProps = (state: ApplicationState) => {
    const location = getLocation(state);
    const parsedQuery = parseQueryString<{ locationId?: string; id?: string }>(location.search);
    const getVmiBinsParameter = {
        vmiLocationId: parsedQuery?.locationId ?? parsedQuery?.id,
        page: 1,
        pageSize: 9999,
    };
    return {
        parsedQuery,
        parsedItems: state.components.vmiBinsImportModal.parsedItems,
        isUploading: state.components.vmiBinsImportModal.isUploading,
        products: state.components.orderUpload.products,
        rowErrors: state.components.orderUpload.rowErrors,
        isBadFile: state.components.vmiBinsImportModal.isBadFile,
        productsProcessed: state.components.orderUpload.productsProcessed,
        uploadLimitExceeded: state.components.vmiBinsImportModal.uploadLimitExceeded,
        isOpen: state.components.vmiBinsImportModal.modalIsOpen,
        vmiBinsDataView: getVmiBinsDataView(state, getVmiBinsParameter),
        getVmiBinsParameter,
    };
};

const mapDispatchToProps = {
    processFile,
    cleanupUploadData,
    batchLoadProducts,
    setUploadLimitExceeded,
    setIsBadFile,
    setIsUploading,
    setVmiBinsImportModalIsOpen,
    addVmiBins,
    loadVmiBins,
};

type Props = ReturnType<typeof mapStateToProps> & ResolveThunks<typeof mapDispatchToProps> & HasToasterContext;

export interface VmiBinsImportModalStyles {
    modal?: ModalPresentationProps;
    container?: GridContainerProps;

    textGridItem?: GridItemProps;
    instructionText?: TypographyPresentationProps;
    templateLink?: LinkPresentationProps;
    controlsGridItem?: GridItemProps;
    fileUploader?: FileUploadPresentationProps;

    buttonsGridItem?: GridItemProps;
    cancelButton?: ButtonPresentationProps;
    importButton?: ButtonPresentationProps;

    dataTable?: DataTableProps;
    tableHead?: DataTableHeadProps;
    itemNumHeader?: DataTableHeaderProps;
    itemNameHeader?: DataTableHeaderProps;
    itemReasonHeader?: DataTableHeaderProps;
    tableBody?: DataTableBodyProps;
    tableRow?: DataTableRowProps;
    itemNumCell?: DataTableCellBaseProps;
    itemNameCell?: DataTableCellBaseProps;
    itemReasonCell?: DataTableCellBaseProps;

    errorLineHeader?: DataTableHeaderProps;
    errorLineCell?: DataTableCellBaseProps;

    tableWrapper?: InjectableCss;
    firstRowOptionCheckbox?: CheckboxPresentationProps;
}

export const vmiBinsImportModalStyles: VmiBinsImportModalStyles = {
    modal: {
        cssOverrides: {
            modalBody: css`
                overflow: visible;
            `,
        },
    },
    container: { gap: 15 },
    controlsGridItem: {
        width: 12,
        css: css`
            display: block;
            max-width: 450px;
            padding-bottom: 40px;
        `,
    },
    textGridItem: {
        width: 12,
        css: css`
            flex-direction: column;
        `,
    },
    instructionText: {
        css: css`
            margin-bottom: 12px;
        `,
    },
    fileUploader: {
        buttonProps: {
            variant: "tertiary",
        },
    },
    buttonsGridItem: {
        css: css`
            justify-content: flex-end;
        `,
        width: 12,
    },
    cancelButton: { variant: "tertiary" },
    importButton: {
        css: css`
            margin-left: 1rem;
        `,
    },
    tableRow: {
        css: css`
            td {
                white-space: normal;
            }
        `,
    },
    tableWrapper: {
        css: css`
            max-height: 400px;
            overflow-y: auto;
            width: 100%;
        `,
    },
    firstRowOptionCheckbox: {
        css: css`
            margin: 15px 0;
            display: block;
        `,
    },
};

const styles = vmiBinsImportModalStyles;

const VmiBinsImportModal = ({
    isOpen,
    toaster,
    parsedQuery,
    parsedItems,
    isUploading,
    products,
    rowErrors,
    isBadFile,
    productsProcessed,
    uploadLimitExceeded,
    processFile,
    cleanupUploadData,
    batchLoadProducts,
    setUploadLimitExceeded,
    setIsBadFile,
    setIsUploading,
    setVmiBinsImportModalIsOpen,
    addVmiBins,
    loadVmiBins,
    getVmiBinsParameter,
    vmiBinsDataView,
}: Props) => {
    const fileUploadRef = useRef({ value: "" } as HTMLInputElement);

    const [file, setFile] = useState<any>(null);
    const [incorrectFileExtension, setIncorrectFileExtension] = React.useState(false);
    const [errorMessages, setErrorMessages] = useState<string[]>([]);
    const { value: vmiLocation } = useContext(VmiLocationStateContext);
    const vmiLocationId = vmiLocation?.id || parsedQuery?.locationId;
    const [importErrors, setImportErrors] = useState<OrderUploadRowError[]>([]);
    const [dataToImport, setDataToImport] = useState<VmiBinModel[]>([]);
    const [showContinueImport, setShowContinueImport] = useState<boolean>(false);
    const [firstRowHeading, setFirstRowHeading] = useState(true);
    const vmiBins = vmiBinsDataView.value;

    useEffect(() => batchGetProducts(), [parsedItems]);

    useEffect(() => processImport(), [productsProcessed, isOpen]);

    useEffect(() => {
        if (!isOpen) {
            resetState();
            cleanupUploadData();
        }
    }, [isOpen]);

    useEffect(() => {
        if (isOpen && !vmiBinsDataView.value && !vmiBinsDataView.isLoading) {
            loadVmiBins(getVmiBinsParameter);
        }
    }, [getVmiBinsParameter]);

    const resetState = () => {
        setFile(null);
        setIncorrectFileExtension(false);
        setErrorMessages([]);
        setUploadLimitExceeded({ uploadLimitExceeded: false });
        setIsBadFile({ isBadFile: false });
        setDataToImport([]);
        setImportErrors([]);
        setShowContinueImport(false);
        setFirstRowHeading(true);
    };

    const closeModalHandler = (isCancel = false) => {
        setIsUploading({ isUploading: false });
        setShowContinueImport(false);

        if (isCancel) {
            if ((rowErrors && rowErrors.length > 0) || importErrors.length > 0) {
                cleanupUploadData();
                setDataToImport([]);
                setImportErrors([]);
                return;
            }

            if (errorMessages.length > 0) {
                setErrorMessages([]);
                return;
            }
        }

        setVmiBinsImportModalIsOpen({ modalIsOpen: false });
    };

    const batchGetProducts = () => {
        cleanupUploadData();
        if (!file) {
            return;
        }

        if (parsedItems.length === 0) {
            setIsBadFile({ isBadFile: true });
            return;
        }

        const extendedNames = parsedItems.map(item => item.name);
        batchLoadProducts({ extendedNames, firstRowHeading, checkInventory: false });
    };

    const fileChangeHandler = (event: React.ChangeEvent<any>) => {
        const uploadedFile = event.target.files[0];
        setFile(uploadedFile);
        if (uploadedFile) {
            const fileExtension = getFileExtension(uploadedFile.name);
            const incorrectFileExtension = ["xls", "xlsx", "csv"].indexOf(fileExtension) === -1;
            setIncorrectFileExtension(incorrectFileExtension);
            setIsBadFile({ isBadFile: incorrectFileExtension });
            setUploadLimitExceeded({ uploadLimitExceeded: false });
        }
    };

    const onImportClick = () => {
        const reader = new FileReader();
        reader.onload = onReaderLoad(getFileExtension(file.name));
        reader.readAsArrayBuffer(file);
    };

    const onReaderLoad = (fileExtension: string) => {
        return (e: Event) => {
            const data = (e.target as any).result;
            processFile({ data, fileExtension, firstRowHeading });
        };
    };

    const processImport = React.useCallback(() => {
        if (!productsProcessed || !isOpen) {
            return;
        }

        if (parsedItems.length > 0) {
            importProducts();
        } else {
            setIsUploading({ isUploading: false });
        }
    }, [productsProcessed, parsedItems, products, rowErrors, isOpen]);

    const importProducts = () => {
        if (!vmiLocationId) {
            return;
        }
        let errors = rowErrors.map(error => ({ ...error }));
        const binsToImport = [];
        if (!showContinueImport) {
            const erpNumberToIdMap: SafeDictionary<string> = {};
            for (const product of products) {
                if (product.erpNumber) {
                    erpNumberToIdMap[product.erpNumber.toLowerCase()] = product.id;
                }

                if (product.customerName) {
                    erpNumberToIdMap[product.customerName.toLowerCase()] = product.id;
                }
            }

            const regexp = /^\d{0,13}(\.\d{1,5})?$/;
            let index = firstRowHeading ? 1 : 0;
            for (const item of parsedItems) {
                index++;
                const binNumber = item.binNumber || item.name;
                const binExisted = vmiBins?.find(bin => bin.binNumber === binNumber.toString());
                const productId = erpNumberToIdMap[item.name.toLowerCase()];
                const product = products?.find(o => o.id === productId);
                const lowerCaseItemUom = item.unitOfMeasure ? item.unitOfMeasure.toLowerCase() : "";
                const uomExisted =
                    lowerCaseItemUom && product && product.productUnitOfMeasures
                        ? product.productUnitOfMeasures.filter(
                              u =>
                                  u.unitOfMeasure.toLowerCase() === lowerCaseItemUom ||
                                  u.unitOfMeasureDisplay.toLowerCase() === lowerCaseItemUom ||
                                  u.description.toLowerCase() === lowerCaseItemUom,
                          )
                        : [];

                const binModel = {
                    productId,
                    binNumber: item.binNumber,
                    minimumQty: Math.min((item.minQty && +item.minQty) || 0, 999999),
                    maximumQty: Math.min((item.maxQty && +item.maxQty) || 0, 999999),
                    lastCountQty: Math.min((item.lastCountQty && +item.lastCountQty) || 0, 999999),
                    unitOfMeasure: uomExisted.length > 0 ? uomExisted[0].unitOfMeasure : item.unitOfMeasure,
                } as VmiBinModel;

                if (!binModel.productId) {
                    continue;
                } else if (errors.some(er => er.index === index)) {
                    continue;
                } else if (binModel.minimumQty < 0) {
                    errors.push({
                        index,
                        error: UploadError.Negative,
                        name: item.name,
                        qtyRequested: 0,
                    });
                } else if (binModel.minimumQty > binModel.maximumQty) {
                    errors.push({
                        index,
                        error: UploadError.MinimumBiggerMaximum,
                        name: item.name,
                        umRequested: "",
                        qtyRequested: 0,
                        qtyOnHand: 0,
                    });
                } else if (binModel.lastCountQty < 0) {
                    errors.push({
                        index,
                        error: UploadError.BinNegativeCount,
                        name: item.name,
                        qtyRequested: 0,
                    });
                } else if (item.lastCountQty && !regexp.test(item.lastCountQty)) {
                    errors.push({
                        index,
                        error: UploadError.WrongType,
                        name: item.name,
                        qtyRequested: 0,
                    });
                } else if (binModel.binNumber && binModel.binNumber.length > 25) {
                    errors.push({
                        index,
                        error: UploadError.Over25Chars,
                        name: item.name,
                        qtyRequested: 0,
                        message: siteMessage(
                            "Vmi_VmiBinNumberMustBeLessThanOrEqualTo25Characters",
                            item.binNumber ?? "",
                        ).toString(),
                    });
                } else if (binExisted) {
                    errors.push({
                        index,
                        error: UploadError.Existed,
                        name: item.name,
                        qtyRequested: 0,
                        message: siteMessage(
                            "Vmi_VmiBinExisted",
                            ...[item.binNumber ?? item.name, item.name],
                        ).toString(),
                    });
                } else {
                    binsToImport.push(binModel);
                }
            }
            errors = errors.sort((a, b) => (a.index < b.index ? -1 : 1));
            setImportErrors(errors);
            setDataToImport(binsToImport);
        }

        if (errors.length > 0 && !showContinueImport) {
            setShowContinueImport(true);
            return;
        }

        const model = showContinueImport ? dataToImport : binsToImport;
        if (model.length <= 0) {
            return;
        }

        addVmiBins({
            vmiLocationId,
            vmiBins: model,
            onComplete: result => {
                setIsUploading({ isUploading: false });
                if (result.apiResult?.successful) {
                    setShowContinueImport(false);
                    setVmiBinsImportModalIsOpen({ modalIsOpen: false });
                    toaster.addToast({
                        body: `${model.length} ${translate("item(s) successfully imported")}`,
                        messageType: "success",
                    });
                } else if (result.apiResult?.errorMessage) {
                    setErrorMessages(result.apiResult?.errorMessage.split("\r\n"));
                }
            },
        });
    };

    const firstRowHeadingChangeHandler: CheckboxProps["onChange"] = (_, value) => setFirstRowHeading(value);

    if (!vmiLocationId) {
        return null;
    }

    return (
        <Modal
            {...styles.modal}
            handleClose={() => closeModalHandler()}
            headline={
                (!importErrors || importErrors.length === 0) && errorMessages.length === 0
                    ? translate("Import CSV")
                    : translate("Import Errors")
            }
            isOpen={isOpen}
            size={600}
        >
            <GridContainer {...styles.container}>
                {(!importErrors || importErrors.length === 0) && errorMessages.length === 0 && (
                    <>
                        <GridItem {...styles.textGridItem}>
                            <Typography as="p" {...styles.instructionText}>
                                {siteMessage("Vmi_BinsImportInstructions_Line1")}
                            </Typography>
                            <Typography as="p" {...styles.instructionText}>
                                {siteMessage("Vmi_BinsImportInstructions_Line2")}
                            </Typography>
                            <Typography as="p" {...styles.instructionText}>
                                <Link
                                    href="/Excel/VMIImport_ProductsTemplate.csv"
                                    target="_blank"
                                    {...styles.templateLink}
                                >
                                    {translate("Download template file")}
                                </Link>
                            </Typography>
                        </GridItem>
                        <GridItem {...styles.controlsGridItem}>
                            <FileUpload
                                {...styles.fileUploader}
                                inputRef={fileUploadRef}
                                fileName={file?.name || ""}
                                accept=".xls,.xlsx,.csv"
                                label={translate("Choose File")}
                                labelPosition="top"
                                error={
                                    incorrectFileExtension
                                        ? translate("must be .xls, .xlsx or .csv")
                                        : uploadLimitExceeded
                                        ? siteMessage("Vmi_BinsImportRowsLimitExceeded")
                                        : isBadFile
                                        ? siteMessage("Vmi_BinsImportUploadError")
                                        : undefined
                                }
                                onFileChange={fileChangeHandler}
                                uid="vmiBinUploadFileUpload"
                            />
                            <Checkbox
                                {...styles.firstRowOptionCheckbox}
                                checked={firstRowHeading}
                                onChange={firstRowHeadingChangeHandler}
                            >
                                {translate("First row is column headings")}
                            </Checkbox>
                        </GridItem>
                    </>
                )}
                {importErrors && importErrors.length > 0 && errorMessages.length === 0 && (
                    <StyledWrapper {...styles.tableWrapper}>
                        <DataTable {...styles.dataTable}>
                            <DataTableHead {...styles.tableHead}>
                                <DataTableHeader {...styles.itemNumHeader}>{translate("Row")}</DataTableHeader>
                                <DataTableHeader {...styles.itemNameHeader}>{translate("Item #")}</DataTableHeader>
                                <DataTableHeader {...styles.itemReasonHeader}>{translate("Reason")}</DataTableHeader>
                            </DataTableHead>
                            <DataTableBody {...styles.tableBody}>
                                {importErrors.map(({ index, name, error, message }) => (
                                    <DataTableRow key={index} {...styles.tableRow} data-test-selector={`vmi-item-row`}>
                                        <DataTableCell {...styles.itemNumCell}>{index}</DataTableCell>
                                        <DataTableCell {...styles.itemNameCell} data-test-selector={`vmi-item-name`}>
                                            {name}
                                        </DataTableCell>
                                        <DataTableCell
                                            {...styles.itemReasonCell}
                                            data-test-selector={`vmi-item-reason`}
                                        >
                                            {error === UploadError.NotEnough &&
                                                siteMessage("QuickOrder_NotEnoughQuantity")}
                                            {error === UploadError.ConfigurableProduct &&
                                                siteMessage("QuickOrder_CannotOrderConfigurable")}
                                            {error === UploadError.StyledProduct &&
                                                siteMessage("QuickOrder_CannotOrderStyled")}
                                            {error === UploadError.Unavailable &&
                                                siteMessage("QuickOrder_ProductIsUnavailable")}
                                            {error === UploadError.InvalidUnit && translate("Invalid U/M")}
                                            {error === UploadError.NotFound && siteMessage("Product_NotFound")}
                                            {error === UploadError.OutOfStock && translate("Out of stock")}
                                            {error === UploadError.Negative &&
                                                siteMessage("Vmi_VmiBinMinimumQuantityMustBeGreaterThanZero")}
                                            {error === UploadError.MinimumBiggerMaximum &&
                                                siteMessage("Vmi_VmiBinMinimumQtyMustBeLessThanOrEqualToMaximumQty")}
                                            {(error === UploadError.Existed || error === UploadError.Over25Chars) &&
                                                message}
                                            {error === UploadError.CountNotProvided &&
                                                siteMessage("Vmi_Count_Not_Provided")}
                                            {error === UploadError.BinNegativeCount &&
                                                siteMessage("Vmi_NegativeCount_BadRequest")}
                                            {error === UploadError.WrongType && siteMessage("Vmi_Count_Wrong_Type")}
                                        </DataTableCell>
                                    </DataTableRow>
                                ))}
                            </DataTableBody>
                        </DataTable>
                    </StyledWrapper>
                )}
                {errorMessages.length > 0 && (
                    <StyledWrapper {...styles.tableWrapper}>
                        <DataTable {...styles.dataTable}>
                            <DataTableHead {...styles.tableHead}>
                                <DataTableHeader {...styles.errorLineHeader}>{translate("Error")}</DataTableHeader>
                            </DataTableHead>
                            <DataTableBody {...styles.tableBody}>
                                {errorMessages.map(message => (
                                    <DataTableRow key={message} {...styles.tableRow}>
                                        <DataTableCell {...styles.errorLineCell}>{message}</DataTableCell>
                                    </DataTableRow>
                                ))}
                            </DataTableBody>
                        </DataTable>
                    </StyledWrapper>
                )}
                <GridItem {...styles.buttonsGridItem}>
                    {!showContinueImport && (
                        <>
                            <Button {...styles.cancelButton} type="button" onClick={() => closeModalHandler(true)}>
                                {translate("Cancel")}
                            </Button>
                            <Button
                                {...styles.importButton}
                                type="button"
                                disabled={!file || isBadFile || isUploading || !vmiBins}
                                onClick={onImportClick}
                                data-test-selector="vmiBinImport"
                            >
                                {translate("Import")}
                            </Button>
                        </>
                    )}
                    {showContinueImport && (
                        <>
                            <Button
                                {...styles.cancelButton}
                                type="button"
                                onClick={() => closeModalHandler(true)}
                                data-test-selector="vmiBinCancelImport"
                            >
                                {translate("Cancel Import")}
                            </Button>
                            <Button
                                {...styles.importButton}
                                type="button"
                                disabled={dataToImport.length === 0}
                                onClick={importProducts}
                                data-test-selector="vmiBinContinueImport"
                            >
                                {translate("Continue Import")}
                            </Button>
                        </>
                    )}
                </GridItem>
            </GridContainer>
        </Modal>
    );
};

export default connect(mapStateToProps, mapDispatchToProps)(withToaster(VmiBinsImportModal));
