import React, { useContext, useEffect, useState } from 'react';
import type { FileRejection } from 'react-dropzone';
import { ErrorCode } from 'react-dropzone';
import type { UploadAreaInfoProps } from '@theorchard/suite-components';
import {
    Alert,
    FullscreenModal,
    UploadArea,
} from '@theorchard/suite-components';
import { ABACUS_ACTION_STATUSES } from '@theorchard/accounting-apps-shared';

import { AbacusDownloadType } from 'src/apollo/definitions/globalTypes';
import { useAbacusDownloadFile } from 'src/apollo/queries/abacus-download';
import { AdjustmentsContext } from 'src/contexts/adjustments-context';
import { uploadFileToS3WithUrl } from 'src/utils/aws';
import { AbacusInitiateFileUploadMutation } from 'src/apollo/queries/abacus-file-upload/__generated__/abacus-initiate-file-upload';
import { useAbacusInitiateFileUpload } from 'src/apollo/queries/abacus-file-upload/abacus-initiate-file-upload';
import { isEmpty } from 'lodash-es';
import { md5 } from 'src/utils/md5';

enum FileErrorCode {
    INVALID_FILE = 'INVALID_FILE',
    TOO_MANY_FILES = 'TOO_MANY_FILES',
}

const AdjustmentsUploadFileScreenWithAbacusFileUpload = () => {
    const { setAdjustmentFile, setModalState } = useContext(AdjustmentsContext);

    const [fileError, setFileError] = useState<FileErrorCode | null>(null);
    const [isUploading, setIsUploading] = useState(false);

    const { initiateFileUpload } = useAbacusInitiateFileUpload('adjustments');

    const { data: templateDownloadFile, getDownloadFile } =
        useAbacusDownloadFile(AbacusDownloadType.ADJUSTMENTS_TEMPLATE, '0');

    const initiateUploadAdjustmentsFileToS3 = async (
        adjustmentsFile: File[],
        fileRejections: FileRejection[]
    ) => {
        if (fileRejections.length) {
            const errorCode = fileRejections[0].errors?.[0].code;

            if (errorCode === ErrorCode.TooManyFiles) {
                setFileError(FileErrorCode.TOO_MANY_FILES);
            } else {
                setFileError(FileErrorCode.INVALID_FILE);
            }

            return;
        }

        if (adjustmentsFile.length > 1) {
            setFileError(FileErrorCode.TOO_MANY_FILES);
            return;
        }

        setFileError(null);

        const file = adjustmentsFile[0];
        const md5sum = await md5(file);

        const initiateUploadResult = await initiateFileUpload(
            file.name,
            file.size,
            md5sum
        );

        if (!isEmpty(initiateUploadResult.errors)) {
            setAdjustmentFile(convertToAdjustmentFile(file));
            setModalState(ABACUS_ACTION_STATUSES.ERROR);
            return;
        }
        if (
            file &&
            initiateUploadResult.data?.abacusInitiateFileUpload &&
            isEmpty(initiateUploadResult.errors)
        ) {
            void uploadFile(file, md5sum, initiateUploadResult.data);
        }
    };

    function convertToAdjustmentFile(file: File | null) {
        return {
            fileName: file?.name,
        };
    }

    const uploadFile = async (
        file: File,
        md5sum: string,
        initiateFileUploadData: AbacusInitiateFileUploadMutation
    ) => {
        if (initiateFileUploadData.abacusInitiateFileUpload) {
            setIsUploading(true);
            try {
                const uploadResponse = await uploadFileToS3WithUrl(
                    file,
                    initiateFileUploadData.abacusInitiateFileUpload
                );

                if (!uploadResponse.ok) {
                    setAdjustmentFile(convertToAdjustmentFile(file));
                    setModalState(ABACUS_ACTION_STATUSES.ERROR);
                    return;
                }
            } catch (error) {
                setAdjustmentFile(convertToAdjustmentFile(file));
                setModalState(ABACUS_ACTION_STATUSES.ERROR);
                return;
            }
            setIsUploading(false);

            const newAdjustmentFile = {
                fileName: file.name,
                md5sum: md5sum,
                uploadFileKey:
                    initiateFileUploadData.abacusInitiateFileUpload.fileKey,
            };

            setAdjustmentFile(newAdjustmentFile);
            setModalState(ABACUS_ACTION_STATUSES.RUNNING);
        }
    };

    useEffect(() => {
        if (templateDownloadFile?.abacusDownload?.url)
            window.location.assign(templateDownloadFile.abacusDownload.url);
    }, [templateDownloadFile]);

    const getUploadAreaInfo = (): UploadAreaInfoProps => {
        switch (fileError) {
            case FileErrorCode.INVALID_FILE:
                return {
                    illustration: 'uploadAreaError',
                    title: 'Upload Failed',
                    description:
                        'Drag and drop or browse from your computer to re-upload a valid file.',
                };
            case FileErrorCode.TOO_MANY_FILES:
                return {
                    illustration: 'uploadAreaError',
                    title: 'Upload Failed',
                    description:
                        'Drag and drop or browse from your computer to re-upload a single file.',
                };
            default:
                return {
                    illustration: 'uploadArea',
                    title: 'Upload Your File',
                    description: 'Drag and drop or browse from your computer.',
                };
        }
    };

    const renderAlert = () => {
        let errorMessage = '';

        switch (fileError) {
            case FileErrorCode.INVALID_FILE:
                errorMessage =
                    "You've tried to upload an invalid file. Please select a single .xlsx file and try uploading again.";
                break;
            case FileErrorCode.TOO_MANY_FILES:
                errorMessage =
                    "You've added too many files. Please select a single .xlsx file and try uploading again.";
                break;
            default:
                return null;
        }

        return (
            <Alert
                testId="UploadAlert"
                variant="error"
                text={
                    <span>
                        <b>Upload Failed.</b>
                        &nbsp;{errorMessage}
                    </span>
                }
            />
        );
    };

    return (
        <>
            <FullscreenModal.Title title="Upload File">
                You can upload an XLSX using the&nbsp;
                <button
                    type="button"
                    onClick={() => void getDownloadFile()}
                    data-testid="AdjustmentsDownloadTemplateButton"
                    className="AdjustmentsImportModal-DownloadTemplateButton"
                >
                    Adjustments Template
                </button>
                &nbsp;with instructions and examples
            </FullscreenModal.Title>
            <FullscreenModal.Body>
                {renderAlert()}
                <UploadArea
                    accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
                    disabled={isUploading}
                    info={getUploadAreaInfo()}
                    inputId="browser-upload"
                    multipleFileSelection={false}
                    onUpload={async (acceptedFiles, fileRejections) =>
                        await initiateUploadAdjustmentsFileToS3(
                            acceptedFiles,
                            fileRejections
                        )
                    }
                    testId="AdjustmentsUploadArea"
                    variant="large"
                />
            </FullscreenModal.Body>
        </>
    );
};

export default AdjustmentsUploadFileScreenWithAbacusFileUpload;
