import { createHandlerChainRunner, HandlerWithResult } from "@insite/client-framework/HandlerCreator";
import logger from "@insite/client-framework/Logger";
import { ParsedItem } from "@insite/client-framework/Store/Components/VmiCountsImportModal/VmiCountsImportModalState";
import { WorkBook } from "xlsx";

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

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

type HandlerType = HandlerWithResult<ProcessFileParameter, ProcessFileResult>;

export const DispatchBeginProcessFile: HandlerType = props => {
    props.dispatch({
        type: "Components/VmiCountsImportModal/BeginProcessFile",
    });
};

export const CreateDefaultResult: HandlerType = props => {
    props.result = {
        parsedItems: [],
        uploadLimitExceeded: false,
        isBadFile: false,
        isUploading: 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, true, props.result);
            }
        } else if (props.parameter.fileExtension === "csv") {
            await processCsv(data, 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.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);

            if (limitExceeded(rows.length, firstRowHeading, result)) {
                return;
            }

            rows.forEach(s => {
                const objectToAdd: ParsedItem = {
                    name: s[0] ?? "",
                    binNumber: s[1] ?? "",
                    count: s[2],
                };

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

const processCsv = async (data: string, 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;
    }

    const rows = results.data;
    const firstRow = rows[0];
    const skipFirstRow =
        firstRow &&
        firstRow.length === 3 &&
        firstRow.every((o: any) => o) &&
        // eslint-disable-next-line no-restricted-globals
        isNaN(parseFloat(firstRow[2]));

    if (limitExceeded(rows.length, skipFirstRow, result)) {
        return;
    }

    rows.forEach((s: (string | null)[], index: number) => {
        if (index === 0 && skipFirstRow) {
            return;
        }

        const objectToAdd: ParsedItem = {
            name: s[0] ?? "",
            binNumber: s[1] ?? "",
            count: s[2],
        };

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

const limitExceeded = (rowsCount: number, skipFirstRow: boolean, result: ProcessFileResult) => {
    const maximumAllowedRowsCount = 500 + (skipFirstRow ? 1 : 0);
    result.uploadLimitExceeded = rowsCount > maximumAllowedRowsCount;
    return result.uploadLimitExceeded;
};

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

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

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