import { emptyGuid, isGuid } from "@insite/client-framework/Common/StringHelpers";
import { createHandlerChainRunner, HandlerWithResult } from "@insite/client-framework/HandlerCreator";
import logger from "@insite/client-framework/Logger";
import { ParsedItem } from "@insite/client-framework/Store/Components/VmiLocationsImportModal/VmiLocationsImportModalState";
import { WorkBook } from "xlsx";

export interface ProcessFileParameter {
    data: ArrayBuffer;
    fileExtension: string;
    firstRowHeading: boolean;
}

export interface ProcessFileResult {
    parsedItems: ParsedItem[];
    uploadLimitExceeded: boolean;
    isBadFile: boolean;
    isUploading: boolean;
    allowCancel: boolean;
}

type HandlerType = HandlerWithResult<ProcessFileParameter, ProcessFileResult>;

export const CreateDefaultResult: HandlerType = props => {
    props.result = {
        parsedItems: [],
        uploadLimitExceeded: false,
        isBadFile: false,
        isUploading: false,
        allowCancel: false,
    };
};
export const ProcessUploadedFile: HandlerType = async props => {
    const XLSX = await import(/* webpackChunkName: "xlsx" */ "xlsx");

    const data = fixData(props.parameter.data);
    try {
        if (props.parameter.fileExtension === "xls" || props.parameter.fileExtension === "xlsx") {
            const workbook = XLSX.read(btoa(data), { type: "base64" });
            if (workbook) {
                await processWorkBook(workbook, props.parameter.firstRowHeading, props.result);
            }
        } else if (props.parameter.fileExtension === "csv") {
            await processCsv(data, props.parameter.firstRowHeading, props.result);
        } else {
            props.result.isBadFile = true;
        }
    } catch (error) {
        logger.error(error);
        props.result.isBadFile = true;
    }

    if (!props.result.isBadFile && !props.result.uploadLimitExceeded) {
        props.result.allowCancel = true;
        props.result.isUploading = true;
    }
};

const fixData = (data: ArrayBuffer) => {
    let o = "";
    let l = 0;
    const w = 10240;
    for (; l < data.byteLength / w; ++l) {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w, l * w + w)) as any);
    }

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w)) as any);

    return o;
};

const processWorkBook = async (workBook: WorkBook, firstRowHeading: boolean, result: ProcessFileResult) => {
    const XLSX = await import(/* webpackChunkName: "xlsx" */ "xlsx");

    result.parsedItems = [];
    workBook.SheetNames.forEach(sheetName => {
        const opts = { header: 1 };
        let rows = XLSX.utils.sheet_to_json<string>(workBook.Sheets[sheetName], opts);
        if (rows.length > 0) {
            if (firstRowHeading) {
                rows = rows.slice(1, rows.length);
            }

            rows = rows.filter(
                r =>
                    (r[0] != null && r[0].length > 0) ||
                    (r[1] != null && r[1].length > 0) ||
                    (r[2] != null && r[2].length > 0),
            );
            if (limitExceeded(rows.length, result)) {
                return;
            }

            rows.forEach(r => {
                const isPrimaryLocation = r[3]?.toLowerCase();
                const useBins = r[4]?.toLowerCase();
                const objectToAdd = {
                    givenBillTo: r[0],
                    billToId: null,
                    givenShipTo: r[1],
                    shipToId: null,
                    name: r[2],
                    isPrimaryLocation: isPrimaryLocation === "true" || isPrimaryLocation === "1",
                    useBins: useBins === "true" || useBins === "1",
                } as ParsedItem;

                if (
                    objectToAdd.givenBillTo &&
                    objectToAdd.givenBillTo !== emptyGuid &&
                    isGuid(objectToAdd.givenBillTo)
                ) {
                    objectToAdd.billToId = objectToAdd.givenBillTo;
                }

                if (
                    objectToAdd.givenShipTo &&
                    objectToAdd.givenShipTo !== emptyGuid &&
                    isGuid(objectToAdd.givenShipTo)
                ) {
                    objectToAdd.shipToId = objectToAdd.givenShipTo;
                }

                result.parsedItems.push(objectToAdd);
            });
        }
    });
};

const processCsv = async (data: string, firstRowHeading: boolean, result: ProcessFileResult) => {
    const { parse } = await import(/* webpackChunkName: "papaparse" */ "papaparse");
    result.parsedItems = [];
    const newLineIndex = data.lastIndexOf("\r\n");
    if (newLineIndex + 2 === data.length) {
        data = data.substr(0, newLineIndex);
    }

    const results = parse(data);
    if (results.errors.length > 0) {
        result.isBadFile = true;
        return;
    }

    let rows = results.data;
    if (firstRowHeading) {
        rows = rows.slice(1, rows.length);
    }
    if (limitExceeded(rows.length, result)) {
        return;
    }

    rows.forEach(s => {
        if (
            (s[0] == null || s[0].length === 0) &&
            (s[1] == null || s[1].length === 0) &&
            (s[2] == null || s[2].length === 0)
        ) {
            return;
        }

        const isPrimaryLocation = s[3]?.toLowerCase();
        const useBins = s[4]?.toLowerCase();
        const objectToAdd = {
            givenBillTo: s[0],
            billToId: null,
            givenShipTo: s[1],
            shipToId: null,
            name: s[2],
            isPrimaryLocation: isPrimaryLocation === "true" || isPrimaryLocation === "1",
            useBins: useBins === "true" || useBins === "1",
        } as ParsedItem;

        if (objectToAdd.givenBillTo && objectToAdd.givenBillTo !== emptyGuid && isGuid(objectToAdd.givenBillTo)) {
            objectToAdd.billToId = objectToAdd.givenBillTo;
        }

        if (objectToAdd.givenShipTo && objectToAdd.givenShipTo !== emptyGuid && isGuid(objectToAdd.givenShipTo)) {
            objectToAdd.shipToId = objectToAdd.givenShipTo;
        }

        result.parsedItems.push(objectToAdd);
    });
};

const limitExceeded = (rowsCount: number, result: ProcessFileResult) => {
    const maximumAllowedRowsCount = 500;
    result.uploadLimitExceeded = rowsCount > maximumAllowedRowsCount;
    return result.uploadLimitExceeded;
};

export const DispatchCompleteProcessFile: HandlerType = props => {
    props.dispatch({
        type: "Components/VmiLocationsImportModal/CompleteProcessFile",
        result: props.result,
    });
};

export const chain = [CreateDefaultResult, ProcessUploadedFile, DispatchCompleteProcessFile];

const processFile = createHandlerChainRunner(chain, "ProcessFile");
export default processFile;
