import { FocusEventHandler, useEffect, useMemo, useState } from "react";
import z from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { FormProvider, useForm, useWatch } from "react-hook-form";
import { Button } from "@gouvfr-lasuite/cunningham-react";
import { Icon, Spinner } from "@gouvfr-lasuite/ui-kit";
import { useTranslation } from "react-i18next";
import { ImportRun, useMailboxesImportsCreate } from "@/features/api/gen";
import { APIError, errorToString } from "@/features/api/api-error";
import MailHelper, { IMAP_DOMAIN_REGEXES } from "@/features/utils/mail-helper";
import { RhfInput } from "@/features/forms/components/react-hook-form";
import { RhfFileUploader } from "@/features/forms/components/react-hook-form/rhf-file-uploader";
import { RhfCheckbox } from "@/features/forms/components/react-hook-form/rhf-checkbox";
import { Banner } from "@/features/ui/components/banner";
import i18n from "@/features/i18n/initI18n";
import { BucketUploadState, useBucketUpload } from "./use-bucket-upload";
import ProgressBar from "@/features/ui/components/progress-bar";

// The form only distinguishes "collecting the import details" (idle) from
// "pushing the archive to storage" (uploading). Once the import run is created,
// the caller returns to the imports list, which tracks server-side progress.
export type ImporterFormStep = "idle" | "uploading";

// An IMAP username is a login (usually, but not necessarily, an email), so we
// only require it to be non-empty rather than a strict email — the backend
// stores it as a free string too. (Auto-discovery below still keys off the
// domain when the username happens to look like an email.)
const usernameSchema = z.string().min(1, { error: i18n.t('Username is required.') });

const importerFormSchema = z.object({
    archive_file: z.array(z.instanceof(File)),
    username: usernameSchema.optional(),
    imap_server: z
        .string()
        .nonempty({ error: i18n.t('IMAP server is required.') })
        .optional(),
    imap_port: z
        .preprocess(
            (value: string | number) => typeof value === 'number' ? value : parseInt(value, 10),
            z.int().min(1).max(65535)
        ).optional(),
    use_ssl: z
        .boolean()
        .optional(),
    password: z
        .string()
        .nonempty({ error: i18n.t('Password is required.') })
        .optional(),
})

type FormFields = z.infer<typeof importerFormSchema>;

type StepFormProps = {
    mailboxId: string;
    onUploading: () => void;
    onSuccess: (importId: string) => void;
    onError: (error: string | null) => void;
    error: string | null;
    step: ImporterFormStep;
}
export const StepForm = ({ mailboxId, onUploading, onSuccess, onError, error, step }: StepFormProps) => {
    const { t } = useTranslation();
    const [showAdvancedImapFields, setShowAdvancedImapFields] = useState(false);
    const [emailDomain, setEmailDomain] = useState<string | undefined>(undefined);
    // Single unified create endpoint (POST /mailboxes/{id}/imports/) for both
    // file and IMAP; it returns the import run to poll (its `id`).
    const importsMutation = useMailboxesImportsCreate({
        mutation: {
            meta: { noGlobalError: true },
            // Surface the backend's discriminated detail (file too large, bad
            // format, upload not found…) — collapsing it to a generic message
            // would send the user re-trying the same doomed upload with no way
            // to learn why it fails.
            onError: (error) => onError(
                error instanceof APIError && error.data
                    ? errorToString(error)
                    : t('An error occurred while importing messages.')
            ),
            onSuccess: (data) => onSuccess((data.data as ImportRun).id),
        }
    });
    const bucketUploadManager = useBucketUpload({
        mailboxId,
        onSuccess: (manager) => importsMutation.mutate({
            mailboxId,
            data: {
                source: "file",
                filename: manager.file!.name,
                file_key: manager.fileKey!,
            }
        }, {
            onSettled: manager.reset,
        }),
        onError: (error) => {
            if (error === 'Aborted') onError(t('You have aborted the upload.'));
            else onError(t('An error occurred while uploading the archive file.'));
        },
    });
    const isBucketUploading = [BucketUploadState.INITIATING, BucketUploadState.IMPORTING, BucketUploadState.COMPLETING].includes(bucketUploadManager.state);
    const isPending = importsMutation.isPending || isBucketUploading;

    const defaultValues = {
        imap_server: '',
        imap_port: 993,
        use_ssl: true,
        username: '',
        password: '',
        archive_file: [],
    }

    const form = useForm({
        resolver: zodResolver(importerFormSchema),
        mode: "onBlur",
        reValidateMode: "onBlur",
        shouldFocusError: false,
        defaultValues
    });
    const archiveFileInputValue = useWatch({
        control: form.control,
        name: 'archive_file'
    });
    // The two import methods are mutually exclusive: picking a file hides the
    // IMAP form, and typing into either IMAP field hides the archive drop zone —
    // so the submit sits right under whichever half the user is filling in.
    const imapValues = useWatch({ control: form.control, name: ['username', 'password'] });
    const isImapStarted = useMemo(
        () => imapValues.some((value) => !!value?.trim()),
        [imapValues],
    );
    const showImapForm = useMemo(() => archiveFileInputValue.length === 0, [archiveFileInputValue]);
    const showArchiveUpload = !isImapStarted;

    /**
     * Try to guess the imap server from the email address
     * If it fails, show all the form fields to invite the user to fill them manually
     */
    const discoverImapServer: FocusEventHandler<HTMLInputElement> = async () => {
        const email = form.getValues("username")!;
        const result = usernameSchema.safeParse(email);

        if (!email || !result.success) return;
        const imapConfig = MailHelper.getImapConfigFromEmail(email);
        const emailDomain = MailHelper.getDomainFromEmail(email);
        setEmailDomain(emailDomain);
        if (!imapConfig) {
            setShowAdvancedImapFields(true);
            form.resetField("imap_server");
            form.resetField("imap_port");
            form.resetField("use_ssl");
            return;
        }
        setShowAdvancedImapFields(false);
        form.setValue("imap_server", imapConfig.host);
        form.setValue("imap_port", imapConfig.port);
        form.setValue("use_ssl", imapConfig.use_ssl);
    };

    /**
     * Exec the mutation to import emails from an IMAP server.
     */
    const importFromImap = async (data: FormFields) => {
        const payload = {
            source: "imap" as const,
            imap_server: data.imap_server!,
            imap_port: data.imap_port!,
            use_ssl: data.use_ssl!,
            username: data.username!,
            password: data.password!,
        }
        return importsMutation.mutateAsync(
            { mailboxId, data: payload }
        );
    }

    /**
     * Exec the mutation to import emails from an Archive file.
     */
    const importFromArchive = async (file: File) => {
        bucketUploadManager.upload(file);
        onUploading();
    }

    /**
     * According to the form data,
     * exec the mutation to import emails from an IMAP server or an Archive file.
     * The unified endpoint returns the created ImportRun; its `id` is what the
     * loader step polls (see the mutation's onSuccess above).
     */
    const handleSubmit = async (data: FormFields) => {
        onError(null);
        if (data.archive_file.length > 0) {
            importFromArchive(data.archive_file[0]);
        } else {
            importFromImap(data);
        }
    };

    useEffect(() => {
        if (!showImapForm) {
            form.setValue('imap_server', undefined, { shouldDirty: true, shouldValidate: true });
            form.setValue('imap_port', undefined, { shouldDirty: true, shouldValidate: true });
            form.setValue('use_ssl', undefined, { shouldDirty: true, shouldValidate: true });
            form.setValue('username', undefined, { shouldDirty: true, shouldValidate: true });
            form.setValue('password', undefined, { shouldDirty: true, shouldValidate: true });
        }
    }, [showImapForm]);

    // Autodiscovery reveals the manual IMAP server fields on blur; clearing the
    // address must take them (and the "allow IMAP access" hint) back down rather
    // than stranding them on screen.
    useEffect(() => {
        if (!imapValues[0]?.trim()) {
            setShowAdvancedImapFields(false);
            setEmailDomain(undefined);
        }
    }, [imapValues]);

    return (
        <FormProvider {...form}>
            <form
                className="import-form"
                onSubmit={form.handleSubmit(handleSubmit)}
                noValidate
            >
                {/* The heading comes from the settings tab title ("Start a new
                    import"), so the form itself stays chrome-free — one less
                    thing pushing the submit off-screen. */}
                {showImapForm === true && (
                    <>
                        <div className="form-field-row">
                            <p>{t('Indicate your old email address and your IMAP password.')}</p>
                        </div>
                        <div className="form-field-row">
                            <RhfInput
                                label={t('Email address or username')}
                                name="username"
                                type="text"
                                text={form.formState.errors.username ? t(form.formState.errors.username.message as string) : undefined}
                                onBlur={discoverImapServer}
                                fullWidth
                            />
                        </div>
                        <div className="form-field-row">
                            <RhfInput
                                label={t('Password')}
                                name="password"
                                type="password"
                                text={form.formState.errors.password ? t(form.formState.errors.password.message as string) : undefined}
                                fullWidth
                            />
                        </div>
                        {
                            showAdvancedImapFields ? (
                                <>
                                    <div className="form-field-row">
                                        <p>{t("We couldn't detect your IMAP server. Please enter it manually.")}</p>
                                    </div>
                                    <div className="form-field-row">
                                        <RhfInput
                                            name="imap_server"
                                            label={t('IMAP server')}
                                            text={form.formState.errors.imap_server ? t(form.formState.errors.imap_server.message as string) : undefined}
                                            fullWidth
                                        />
                                        <RhfInput
                                            name="imap_port"
                                            type="number"
                                            min={1}
                                            max={65535}
                                            label={t('IMAP port')}
                                            text={form.formState.errors.imap_port ? t(form.formState.errors.imap_port.message as string) : undefined}
                                            fullWidth
                                        />
                                    </div>
                                    <div className="form-field-row">
                                        <RhfCheckbox
                                            label={t("Use SSL")}
                                            name="use_ssl"
                                            fullWidth
                                        />
                                    </div>
                                </>
                            ) : (
                                <>
                                    <input type="hidden" {...form.register('imap_server')} />
                                    <input type="hidden" {...form.register('imap_port')} />
                                    <input type="hidden" {...form.register('use_ssl')} />
                                </>
                            )
                        }
                        {
                            emailDomain && (
                                <Banner type="info">
                                    <p>{t('To be able to import emails from an IMAP server, you may need to allow IMAP access on your account.')}</p>
                                    <p><LinkToDoc imapDomain={emailDomain} /></p>
                                </Banner>
                            )
                        }
                        {showArchiveUpload && (
                            <div className="form-field-row flex-justify-center import-form__or-separator">
                                <p>{t('Or')}</p>
                            </div>
                        )}
                    </>
                )}
                {showArchiveUpload && (
                    <>
                        {step !== 'uploading' && (
                        <div className="form-field-row">
                            <p>{t('Upload an archive')}</p>
                        </div>
                        )}
                        <div className="form-field-row import-form__archive">
                            <RhfFileUploader
                                name="archive_file"
                                accept=".eml,.mbox,.pst"
                                icon={<Icon name="inventory_2" />}
                                fileSelectedIcon={<Icon name="inventory_2" />}
                                bigText={t('Drag and drop an archive here')}
                                text={t('EML, MBOX or PST')}
                                fullWidth
                            />
                            {[BucketUploadState.INITIATING, BucketUploadState.IMPORTING, BucketUploadState.COMPLETING, BucketUploadState.COMPLETED].includes(bucketUploadManager.state) && (
                                <div className="progress-bar-container">
                                    <ProgressBar progress={bucketUploadManager.progress} />
                                    <p>{t('Uploading... {{progress}}%', { progress: bucketUploadManager.progress })}</p>
                                </div>
                            )}
                        </div>
                    </>
                )}
                {error && (<Banner type="error"><p>{t(error)}</p></Banner>)}
                <div className="form-field-row">
                    {[BucketUploadState.IMPORTING].includes(bucketUploadManager.state) ? (
                        // Distinct `key` from the submit button below: without it React
                        // reuses the same <button> DOM node across the ternary and merely
                        // patches its `type`. Aborting leaves IMPORTING synchronously
                        // (discrete-event flush), so the node's `type` flips to "submit"
                        // *during* this very click — the browser then fires the form's
                        // default submit, which restarts the upload from 0%. preventDefault
                        // is the belt-and-suspenders guard against that default action.
                        <Button
                            key="abort"
                            type="button"
                            onClick={(e) => {
                                e.preventDefault();
                                bucketUploadManager.abort();
                            }}
                            color="brand"
                            variant="tertiary"
                            fullWidth
                        >
                            {t('Abort upload')}
                        </Button>

                    ) : (
                        <Button
                            key="import"
                            type="submit"
                            aria-busy={isPending}
                            disabled={isPending}
                            icon={isPending ? <Spinner size="sm" /> : undefined}
                            fullWidth
                        >
                            {t('Import')}
                        </Button>
                    )}
                </div>
            </form>
        </FormProvider>
    );
};


const LinkToDoc = ({ imapDomain }: { imapDomain: string }) => {
    const { t } = useTranslation();
    const domainDoc = {
        [IMAP_DOMAIN_REGEXES.get("gmail")!]: {
            displayName: "Gmail",
            href: "https://support.google.com/accounts/answer/185833"
        },
        [IMAP_DOMAIN_REGEXES.get("orange")!]: {
            displayName: "Orange",
            href: "https://assistance.orange.fr/ordinateurs-peripheriques/installer-et-utiliser/l-utilisation-du-mail-et-du-cloud/mail-orange/le-mail-orange-nouvelle-version/parametrer-la-boite-mail/mail-orange-comment-activer-ou-desactiver-les-acces-pop-imap-pour-les-logiciels-ou-applications-de-messagerie-tiers_427398-957069"
        },
        [IMAP_DOMAIN_REGEXES.get("wanadoo")!]: {
            displayName: "Wanadoo",
            href: "https://assistance.orange.fr/ordinateurs-peripheriques/installer-et-utiliser/l-utilisation-du-mail-et-du-cloud/mail-orange/le-mail-orange-nouvelle-version/parametrer-la-boite-mail/mail-orange-comment-activer-ou-desactiver-les-acces-pop-imap-pour-les-logiciels-ou-applications-de-messagerie-tiers_427398-957069"
        },
        [IMAP_DOMAIN_REGEXES.get("yahoo")!]: {
            displayName: "Yahoo!",
            href: "https://fr.aide.yahoo.com/kb/SLN4075.html?activity=yhelp-signin&guccounter=1&guce_referrer=aHR0cHM6Ly9sb2dpbi55YWhvby5jb20v&guce_referrer_sig=AQAAAL_UYOz8UEdd09wJ1xwaD2Wk7ZEVJTpLoR2yd3KnPbAE4SJGaRT33BA_kqufMpZRtaNzcoOlt7D8hHCog4XCzkqWNwQTfq8pQCqNk3PxxeZ-SwPx_gNC7wl6aPZ_f7JDM-_Co419TiTtNKwZ2f2cxleG_AqbLPzRblPmozI3STS0"
        }
    }
    const doc = Array.from(Object.entries(domainDoc)).find(([regex]) => new RegExp(`^${regex}$`).test(imapDomain))?.[1];

    if (!doc) return null;
    return <a href={doc.href} target="_blank" rel="noreferrer noopener">{t('How to allow IMAP connections from your account {{name}}?', { name: doc.displayName })}</a>
}
