import { createHandlerChainRunner, HandlerWithResult } from "@insite/client-framework/HandlerCreator";
import { API_URL_CURRENT_FRAGMENT, ServiceResult } from "@insite/client-framework/Services/ApiService";
import { getBillTo } from "@insite/client-framework/Services/CustomersService";
import {
    batchCreateVmiLocations,
    BatchCreateVmiLocationsApiParameter,
    getVmiLocations,
    GetVmiLocationsApiParameter,
} from "@insite/client-framework/Services/VmiLocationService";
import {
    ParsedItem,
    VmiLocationUploadRowError,
} from "@insite/client-framework/Store/Components/VmiLocationsImportModal/VmiLocationsImportModalState";
import { BillToModel, VmiLocationCollectionModel, VmiLocationModel } from "@insite/client-framework/Types/ApiModels";

export const enum VmiLocationUploadError {
    None,
    NoBillTo,
    BillToNotFound,
    NoShipTo,
    ShipToNotFound,
    NoLocationName,
    DuplicateLocationName,
    DuplicateLocationNameSameFile,
    LocationNameTooLong,
}

export interface BatchCreateVmiLocationsParameter {
    firstRowHeading: boolean;
    validationOnly: boolean;
}

export interface BatchCreateVmiLocationsResult {
    apiParameter: BatchCreateVmiLocationsApiParameter;
    apiResult: ServiceResult<VmiLocationCollectionModel>;
    vmiLocations: VmiLocationModel[];
    errorEntries: VmiLocationUploadRowError[];
    importedItems: VmiLocationModel[];
    currentBillTo: BillToModel | undefined;
    getVmiLocationsParameter: GetVmiLocationsApiParameter;
}

type HandlerType = HandlerWithResult<BatchCreateVmiLocationsParameter, BatchCreateVmiLocationsResult>;

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

export const InitializeResult: HandlerType = props => {
    props.result = {
        apiParameter: { validationOnly: false, vmiLocations: [] },
        apiResult: { successful: false, errorMessage: "" },
        vmiLocations: [],
        errorEntries: [],
        importedItems: [],
        currentBillTo: undefined,
        getVmiLocationsParameter: {
            page: 1,
            pageSize: 9999,
            expand: ["customerlabel"],
        },
    };
};

export const PopulateApiParameter: HandlerType = props => {
    const state = props.getState();
    const parsedItems = state?.components.vmiLocationsImportModal.parsedItems;
    if (parsedItems.length === 0) {
        props.dispatch({
            type: "Components/VmiLocationsImportModal/SetIsBadFile",
            parameter: { isBadFile: true },
        });
        return false;
    }

    props.result.apiParameter = {
        vmiLocations: parsedItems.map(
            item =>
                ({
                    name: item.name,
                    billToId: item.billToId || undefined,
                    shipToId: item.shipToId,
                    isPrimaryLocation: item.isPrimaryLocation,
                    useBins: item.useBins,
                } as VmiLocationModel),
        ),
        validationOnly: props.parameter.validationOnly,
    };

    props.result.getVmiLocationsParameter.userId = state?.pages.vmiUsers.getVmiLocationsParameter.userId;
};

export const RequestDataFromApi: HandlerType = async props => {
    props.result.apiResult = await batchCreateVmiLocations(props.result.apiParameter);
};

export const LoadBillTo: HandlerType = async props => {
    if (!props.result.apiResult.successful) {
        props.dispatch({
            type: "Components/VmiLocationsImportModal/SetFullFileError",
            fullFileError: false,
        });
        return false;
    }

    const state = props.getState();
    if (state.components.vmiLocationsImportModal.uploadCancelled) {
        return false;
    }

    props.result.currentBillTo = await getBillTo({
        billToId: API_URL_CURRENT_FRAGMENT,
        expand: ["shipTos", "excludeOneTime", "excludeCreateNew"],
    });
};

export const ProcessLocations: HandlerType = async props => {
    const {
        getState,
        parameter: { firstRowHeading },
        result: { currentBillTo, errorEntries, importedItems, apiResult },
    } = props;
    if (!apiResult.successful) {
        return;
    }

    const parsedItems = getState()?.components.vmiLocationsImportModal.parsedItems;
    const apiVmiLocationResult = await getVmiLocations(props.result.getVmiLocationsParameter);
    for (let i = 0; i < parsedItems.length; i++) {
        const index = firstRowHeading ? i + 2 : i + 1;
        const parsedItem = parsedItems[i];
        const error = validateLocationData(parsedItem, apiVmiLocationResult.vmiLocations, currentBillTo, importedItems);
        if (error === VmiLocationUploadError.None) {
            importedItems.push({
                name: parsedItem.name,
                billToId: parsedItem.billToId,
                shipToId: parsedItem.shipToId,
                isPrimaryLocation: parsedItem.isPrimaryLocation,
                useBins: parsedItem.useBins,
            } as VmiLocationModel);
        } else {
            errorEntries.push(
                mapLocationErrorInfo(index, error, parsedItem.givenBillTo, parsedItem.givenShipTo, parsedItem.name),
            );
        }
    }
};

const validateLocationData = (
    row: ParsedItem,
    locations: VmiLocationModel[] | null,
    currentBillTo: BillToModel | undefined,
    importedItems: VmiLocationModel[],
) => {
    if (!row.givenBillTo) {
        return VmiLocationUploadError.NoBillTo;
    }
    if (row.billToId === null) {
        return VmiLocationUploadError.BillToNotFound;
    }
    if (row.billToId.toLowerCase() !== currentBillTo?.id.toString().toLowerCase()) {
        return VmiLocationUploadError.BillToNotFound;
    }
    if (!row.givenShipTo) {
        return VmiLocationUploadError.NoShipTo;
    }
    if (row.shipToId === null) {
        return VmiLocationUploadError.ShipToNotFound;
    }
    if (!currentBillTo.shipTos?.find(x => x.id.toLowerCase() === row.shipToId?.toLowerCase())) {
        return VmiLocationUploadError.ShipToNotFound;
    }
    if (!row.name) {
        return VmiLocationUploadError.NoLocationName;
    }
    if (row.name.length > 255) {
        return VmiLocationUploadError.LocationNameTooLong;
    }

    const existingVmiLocation = locations?.filter(
        l =>
            l?.name?.toLowerCase() === row.name.toLowerCase() &&
            l?.shipToId?.toString().toLowerCase() === row.shipToId?.toString().toLowerCase(),
    );
    if (existingVmiLocation && existingVmiLocation.length > 0) {
        return VmiLocationUploadError.DuplicateLocationName;
    }

    if (
        importedItems.find(
            x =>
                x.name.toLowerCase() === row.name.toLowerCase() &&
                x.shipToId?.toString().toLowerCase() === row.shipToId?.toString().toLowerCase(),
        )
    ) {
        return VmiLocationUploadError.DuplicateLocationNameSameFile;
    }

    return VmiLocationUploadError.None;
};

const mapLocationErrorInfo = (
    index: number,
    error: VmiLocationUploadError,
    billToId: string | null,
    shipToId: string | null,
    name: string,
) => {
    return {
        billToId,
        shipToId,
        name,
        index,
        error,
    };
};

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

export const chain = [
    DispatchBeginBatchLoadProducts,
    InitializeResult,
    PopulateApiParameter,
    RequestDataFromApi,
    LoadBillTo,
    ProcessLocations,
    DispatchCompleteBatchLoadProducts,
];

const validateVmiLocations = createHandlerChainRunner(chain, "ValidateVmiLocations");
export default validateVmiLocations;
