import React, { useRef, useState } from "react";
import { Room } from "matrix-js-sdk/src";
import { _t } from "../../../languageHandler";
import { IDialogProps } from "./IDialogProps";
import BaseDialog from "./BaseDialog";
import DialogButtons from "../elements/DialogButtons";
import Field from "../elements/Field";
import StyledRadioGroup from "../elements/StyledRadioGroup";
import StyledCheckbox from "../elements/StyledCheckbox";
import {
    exportFormats,
    exportTypes,
    textForFormat,
    textForType,
} from "../../../utils/exportUtils/exportUtils";
import { IFieldState, IValidationResult } from "../elements/Validation";
import HTMLExporter from "../../../utils/exportUtils/HtmlExport";
import JSONExporter from "../../../utils/exportUtils/JSONExport";
import PlainTextExporter from "../../../utils/exportUtils/PlainTextExport";
import { useStateCallback } from "../../../hooks/useStateCallback";

interface IProps extends IDialogProps {
    room: Room;
}

const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
    const [exportFormat, setExportFormat] = useState(exportFormats.HTML);
    const [exportType, setExportType] = useState(exportTypes.TIMELINE);
    const [includeAttachments, setAttachments] = useState(false);
    const [isExporting, setExporting] = useState(false);
    const [numberOfMessages, setNumberOfMessages] = useState<number>(100);
    const [sizeLimit, setSizeLimit] = useState<number | null>(8);
    const sizeLimitRef = useRef<Field>();
    const messageCountRef = useRef<Field>();
    const [displayCancel, setCancelWarning] = useState(false);
    const [exportCancelled, setExportCancelled] = useState(false);
    const [exportSuccessful, setExportSuccessful] = useState(false);
    const [Exporter, setExporter] = useStateCallback(
        null,
        async (Exporter: HTMLExporter | PlainTextExporter | JSONExporter) => {
            await Exporter?.export().then(() => {
                if (!exportCancelled) setExportSuccessful(true);
            });
        },
    );

    const startExport = async () => {
        const exportOptions = {
            numberOfMessages,
            attachmentsIncluded: includeAttachments,
            maxSize: sizeLimit * 1024 * 1024,
        };
        switch (exportFormat) {
            case exportFormats.HTML:
                setExporter(
                    new HTMLExporter(
                        room,
                        exportTypes[exportType],
                        exportOptions,
                    ),
                );
                break;
            case exportFormats.JSON:
                setExporter(
                    new JSONExporter(
                        room,
                        exportTypes[exportType],
                        exportOptions,
                    ),
                );
                break;
            case exportFormats.PLAIN_TEXT:
                setExporter(
                    new PlainTextExporter(
                        room,
                        exportTypes[exportType],
                        exportOptions,
                    ),
                );
                break;
            default:
                console.error("Unknown export format");
                return;
        }
    };

    const onExportClick = async () => {
        const isValidSize = await sizeLimitRef.current.validate({
            focused: false,
        });
        if (!isValidSize) {
            sizeLimitRef.current.validate({ focused: true });
            return;
        }
        if (exportType === exportTypes.LAST_N_MESSAGES) {
            const isValidNumberOfMessages =
                await messageCountRef.current.validate({ focused: false });
            if (!isValidNumberOfMessages) {
                messageCountRef.current.validate({ focused: true });
                return;
            }
        }
        setExporting(true);
        await startExport();
    };

    const onValidateSize = async ({
        value,
    }: Pick<IFieldState, "value">): Promise<IValidationResult> => {
        const parsedSize = parseFloat(value);
        const min = 1;
        const max = 4000;

        if (isNaN(parsedSize)) {
            return { valid: false, feedback: _t("Size must be a number") };
        }

        if (!(min <= parsedSize && parsedSize <= max)) {
            return {
                valid: false,
                feedback: _t(
                    "Size can only be between %(min)s MB and %(max)s MB",
                    { min, max },
                ),
            };
        }

        return {
            valid: true,
            feedback: _t("Enter size between %(min)s MB and %(max)s MB", {
                min,
                max,
            }),
        };
    };

    const onValidateNumberOfMessages = async ({
        value,
    }: Pick<IFieldState, "value">): Promise<IValidationResult> => {
        const parsedSize = parseFloat(value);
        const min = 1;
        const max = 10 ** 8;

        if (isNaN(parsedSize)) {
            return {
                valid: false,
                feedback: _t("Number of messages must be a number"),
            };
        }

        if (!(min <= parsedSize && parsedSize <= max)) {
            return {
                valid: false,
                feedback: _t(
                    "Number of messages can only be between %(min)s and %(max)s",
                    { min, max },
                ),
            };
        }

        return {
            valid: true,
            feedback: _t("Enter a number between %(min)s and %(max)s", {
                min,
                max,
            }),
        };
    };

    const onCancel = async () => {
        if (isExporting) setCancelWarning(true);
        else onFinished(false);
    };

    const confirmCanel = async () => {
        await Exporter?.cancelExport().then(() => {
            setExportCancelled(true);
            setExporting(false);
            setExporter(null);
        });
    };

    const exportFormatOptions = Object.keys(exportFormats).map((format) => ({
        value: format,
        label: textForFormat(format),
    }));

    const exportTypeOptions = Object.keys(exportTypes).map((type) => {
        return (
            <option key={type} value={type}>
                {textForType(type)}
            </option>
        );
    });
    let MessageCount = null;
    if (exportType === exportTypes.LAST_N_MESSAGES) {
        MessageCount = (
            <Field
                element="input"
                type="number"
                value={numberOfMessages}
                ref={messageCountRef}
                onValidate={onValidateNumberOfMessages}
                label={_t("Number of messages")}
                onChange={(e) => {
                    setNumberOfMessages(parseInt(e.target.value));
                }}
            />
        );
    }

    const sizePostFix = <span title={_t("MB")}>{_t("MB")}</span>;

    const ExportCancelWarning = (
        <BaseDialog
            title={_t("Warning")}
            className="mx_ExportDialog"
            contentId="mx_Dialog_content"
            onFinished={onFinished}
            fixedWidth={true}
        >
            <p>
                {" "}
                {_t(
                    "Are you sure you want to stop exporting your data? If you do, you'll need to start over.",
                )}{" "}
            </p>
            <DialogButtons
                primaryButton={_t("Abort export process")}
                primaryButtonClass="danger"
                hasCancel={true}
                cancelButton={_t("Continue")}
                onCancel={() => setCancelWarning(false)}
                onPrimaryButtonClick={confirmCanel}
            />
        </BaseDialog>
    );

    const ExportSettings = (
        <BaseDialog
            title={_t("Export Chat")}
            className="mx_ExportDialog"
            contentId="mx_Dialog_content"
            hasCancel={true}
            onFinished={onFinished}
            fixedWidth={true}
        >
            <p>
                {_t(
                    "Select from the options below to export chats from your timeline",
                )}
            </p>

            <span className="mx_ExportDialog_subheading">{_t("Format")}</span>

            <StyledRadioGroup
                name="feedbackRating"
                value={exportFormat}
                onChange={(key) => setExportFormat(exportFormats[key])}
                definitions={exportFormatOptions}
            />

            <span className="mx_ExportDialog_subheading">{_t("Messages")}</span>

            <Field
                element="select"
                value={exportType}
                onChange={(e) => {
                    setExportType(exportTypes[e.target.value]);
                }}
            >
                {exportTypeOptions}
            </Field>
            {MessageCount}

            <span className="mx_ExportDialog_subheading">
                {_t("Size Limit")}
            </span>

            <Field
                type="number"
                autoComplete="off"
                onValidate={onValidateSize}
                element="input"
                ref={sizeLimitRef}
                value={sizeLimit}
                postfixComponent={sizePostFix}
                onChange={(e) => setSizeLimit(parseInt(e.target.value))}
            />

            <StyledCheckbox
                checked={includeAttachments}
                onChange={(e) =>
                    setAttachments((e.target as HTMLInputElement).checked)
                }
            >
                {_t("Include Attachments")}
            </StyledCheckbox>

            <DialogButtons
                primaryButton={_t("Export")}
                onPrimaryButtonClick={onExportClick}
                onCancel={() => onFinished(false)}
            />
        </BaseDialog>
    );

    const ExportSuccessful = (
        <BaseDialog
            title={_t("Export Successful")}
            className="mx_ExportDialog"
            contentId="mx_Dialog_content"
            onFinished={onFinished}
            fixedWidth={true}
        >
            <p> {_t("Your messages were successfully exported")} </p>

            <DialogButtons
                primaryButton={_t("Okay")}
                hasCancel={false}
                onPrimaryButtonClick={onFinished}
            />
        </BaseDialog>
    );

    const ExportCancelSuccess = (
        <BaseDialog
            title={_t("Export Cancelled")}
            className="mx_ExportDialog"
            contentId="mx_Dialog_content"
            onFinished={onFinished}
            fixedWidth={true}
        >
            <p> {_t("The export was cancelled successfully")} </p>

            <DialogButtons
                primaryButton={_t("Okay")}
                hasCancel={false}
                onPrimaryButtonClick={onFinished}
            />
        </BaseDialog>
    );

    const ExportProgress = (
        <BaseDialog
            title={_t("Exporting your data...")}
            className="mx_ExportDialog"
            contentId="mx_Dialog_content"
            onFinished={onFinished}
            fixedWidth={true}
        >
            <DialogButtons
                primaryButton={_t("Cancel")}
                primaryButtonClass="danger"
                hasCancel={false}
                onPrimaryButtonClick={onCancel}
            />
        </BaseDialog>
    );

    let componentToDisplay: JSX.Element;
    if (exportCancelled) componentToDisplay = ExportCancelSuccess;
    else if (exportSuccessful) componentToDisplay = ExportSuccessful;
    else if (!isExporting) componentToDisplay = ExportSettings;
    else if (displayCancel) componentToDisplay = ExportCancelWarning;
    else componentToDisplay = ExportProgress;

    return componentToDisplay;
};

export default ExportDialog;
