import type { FC } from 'react';
import React, { useCallback, useState } from 'react';
import {
    LoadingSpinner,
    Alert,
    UploadArea,
} from '@theorchard/suite-components';
import { useIdentity } from '@theorchard/suite-frontend';
import { useLazyOwnershipUploadToken } from 'src/data/queries';
import { normalizeFilename } from 'src/data/utils/formatter';
import { uploadFileToS3 } from 'src/utils/aws';
import { useOwnershipUpload } from 'src/utils/features';
import {
    recordUploadFile,
    recordTemplateDownloadClick,
} from 'src/utils/segment/clickEvents';
import { CORRECT_COLUMN_HEADERS, TOTAL_COLUMN_COUNT } from './constants';
import UploadGlyph from './uploadIcon';
import { validateCsvData, validateFileUpload } from './validator';
import type { FileError } from './validator';
import type { OwnershipUploadToken as OnwershipToken } from 'src/data/queries/ownershipUploadToken/__generated__/OwnershipUploadToken';

export const CLASSNAME = 'UploadBox';

export interface Props {
    accept: string[];
    handleFileErrors: (err: FileError) => void;
    handleS3Uploaded: (filename: string | null) => void;
}

const UploadBox: FC<Props> = ({
    accept,
    handleFileErrors,
    handleS3Uploaded,
}) => {
    const { profileId, id, profileUUID } = useIdentity();
    const [doGetOwnershipUploadToken, { data: uploadToken }] =
        useLazyOwnershipUploadToken({
            originalFilename: '',
        });

    const [validFile, setValidFile] = useState<File>();
    const [startS3FileUpload, setStartS3FileUpload] = useState<boolean>(false);
    const [isS3Uploading, setIsS3Uploading] = useState<boolean>(false);
    const [showUpload, setShowUpload] = useState<boolean>(useOwnershipUpload());
    const handleFileTypeError = (item?: FileError[]) => {
        if (!item) return;

        handleFileErrors(item[0]); // Return error for that single file.
    };

    const handleFileValidation = useCallback(
        async (items?: File[]) => {
            if (!items || items.length < 1) return;
            const validationErrors = validateFileUpload(items[0]);
            if (!validationErrors) {
                const schemaErrors = await validateCsvData(
                    items[0],
                    CORRECT_COLUMN_HEADERS,
                    TOTAL_COLUMN_COUNT
                );
                if (schemaErrors)
                    handleFileErrors({ errors: schemaErrors, file: items[0] });
                else {
                    const f = normalizeFilename(items[0].name);
                    if (f.length > 1) {
                        await doGetOwnershipUploadToken({
                            variables: { originalFilename: f },
                        });
                    }
                    setValidFile(items[0]);
                }
            } else {
                handleFileErrors({ errors: validationErrors, file: items[0] });
            }
        },
        [handleFileErrors, setValidFile, doGetOwnershipUploadToken]
    );

    const onStartUpload = (files: File[]) => {
        void setShowUpload(false);
        void handleFileValidation(files);
        setStartS3FileUpload(true);
    };

    if (!isS3Uploading && validFile && uploadToken && startS3FileUpload) {
        setIsS3Uploading(true);
        const thenCallback = () => {
            setStartS3FileUpload(false);
            setIsS3Uploading(false);
        };

        const catchCallback = (error: Error) => {
            handleFileErrors({
                errors: [{ message: error.message, code: error.name }],
                file: validFile,
            });
        };

        const finallyCallback = (key: string) => {
            handleS3Uploaded(key);
            recordUploadFile();
        };

        if (uploadToken) {
            const token: OnwershipToken['nrOwnershipUploadToken'] =
                uploadToken.nrOwnershipUploadToken;
            const fileExtension = validFile.name?.split('.').pop();
            const key = `${token.filename}.${fileExtension}`;
            const filename = `${validFile.name}`;
            void uploadFileToS3(
                validFile,
                token.bucket,
                key,
                token.credentials,
                {
                    filename,
                    profileId: `${profileId}`,
                    identityId: `${id}`,
                    profileUUID: `${profileUUID}`,
                }
            )
                .catch(catchCallback)
                .then(thenCallback)
                .finally(finallyCallback.bind(null, key));
        }
    }

    return (
        <div>
            {!isS3Uploading && (
                <div className={`${CLASSNAME}-instructions`}>
                    <p>
                        {$t(`ownershipRights.uploadBox.uploadCsvInstructions`)}
                        <a
                            onClick={recordTemplateDownloadClick}
                            href="https://cdn.theorchard.io/assets/frontend-content/blank_ownership_audio_template.csv"
                        >
                            <b>
                                <i data-testid="templateLink">
                                    {$t(
                                        `ownershipRights.uploadBox.templateLink`
                                    )}
                                </i>
                            </b>
                        </a>
                    </p>
                </div>
            )}
            {isS3Uploading && (
                <Alert
                    variant="information"
                    text={
                        <span>
                            {$t(`ownershipRights.uploadBox.stayOnPage`)}
                        </span>
                    }
                />
            )}
            <UploadArea
                onUpload={onStartUpload}
                onDropRejected={handleFileTypeError}
                accept={accept}
                maxFiles={1}
                multipleFileSelection={false}
                className={CLASSNAME}
                inputId="csv-browser-upload"
                disabled={!showUpload}
            >
                {!showUpload && (
                    <div
                        className={`${CLASSNAME}-s3-uploading`}
                        data-testid="hide-upload"
                    >
                        <div className={`${CLASSNAME}-hide-uploading-message`}>
                            <h2 data-testid="hide-uploading-message">
                                {$t(`ownershipRights.uploadBox.hideUpload`)}
                            </h2>
                        </div>
                    </div>
                )}
                {!isS3Uploading && !startS3FileUpload && showUpload && (
                    <div
                        className={`${CLASSNAME}-area`}
                        data-testid="upload-area"
                    >
                        <h2 className={`${CLASSNAME}-header-text`}>
                            {$t(`ownershipRights.uploadBox.uploadYourFile`)}
                        </h2>
                        <div className={`${CLASSNAME}-help-text`}>
                            {$t(`ownershipRights.uploadBox.dragDropMessage`)}
                            <label
                                data-testid={CLASSNAME}
                                htmlFor="csv-browser-upload"
                                className={`${CLASSNAME}-browse-link`}
                            >
                                {$t(`ownershipRights.uploadBox.browse`)}
                            </label>
                            {$t(`ownershipRights.uploadBox.fromComputer`)}
                        </div>
                        <UploadGlyph />
                    </div>
                )}
                {isS3Uploading && startS3FileUpload && (
                    <div
                        className={`${CLASSNAME}-s3-uploading`}
                        data-testid="uploading-file"
                    >
                        <div className={`${CLASSNAME}-uploading-message`}>
                            <h2>{$t(`ownershipRights.uploadBox.uploading`)}</h2>
                            <LoadingSpinner show />
                        </div>
                        <div className={`${CLASSNAME}-uploading-files`}>
                            <p>
                                {$t(`ownershipRights.uploadBox.runningReview`)}{' '}
                                {validFile?.name}
                            </p>
                        </div>
                    </div>
                )}
            </UploadArea>
        </div>
    );
};

export default UploadBox;
