import React, { useEffect, useState } from 'react';
import Modal, { RequiredModalProps } from '@/components/elements/Modal';
import { Field as FormikField, Form, Formik, FormikHelpers, useFormikContext } from 'formik';
import { object, string } from 'yup';
import Field from '@/components/elements/Field';
import FormikFieldWrapper from '@/components/elements/FormikFieldWrapper';
import useFlash from '@/plugins/useFlash';
import useServer from '@/plugins/useServer';
import createServerBackup from '@/api/server/backups/createServerBackup';
import { httpErrorToHuman } from '@/api/http';
import FlashMessageRender from '@/components/FlashMessageRender';
import { ServerBackup } from '@/api/server/backups/getServerBackups';

interface Values {
    name: string;
    ignored: string;
}

interface Props {
    onBackupGenerated: (backup: ServerBackup) => void;
}

const ModalContent = ({ ...props }: RequiredModalProps) => {
    const { isSubmitting } = useFormikContext<Values>();

    return (
        <Modal {...props} showSpinnerOverlay={isSubmitting}>
            <Form className={'pb-6'}>
                <FlashMessageRender byKey={'backups:create'} className={'mb-4'}/>
                <h3 className={'mb-6'}>Create server backup</h3>
                <div className={'mb-6'}>
                    <Field
                        name={'name'}
                        label={'Backup name'}
                        description={'If provided, the name that should be used to reference this backup.'}
                    />
                </div>
                <div className={'mb-6'}>
                    <FormikFieldWrapper
                        name={'ignore'}
                        label={'Ignored Files & Directories'}
                        description={`
                            Enter the files or folders to ignore while generating this backup. Leave blank to use
                            the contents of the .pteroignore file in the root of the server directory if present.
                            Wildcard matching of files and folders is supported in addition to negating a rule by
                            prefixing the path with an exclamation point.
                        `}
                    >
                        <FormikField
                            name={'contents'}
                            component={'textarea'}
                            className={'input-dark h-32'}
                        />
                    </FormikFieldWrapper>
                </div>
                <div className={'flex justify-end'}>
                    <button
                        type={'submit'}
                        className={'btn btn-primary btn-sm'}
                    >
                        Start backup
                    </button>
                </div>
            </Form>
        </Modal>
    );
};

export default ({ onBackupGenerated }: Props) => {
    const { uuid } = useServer();
    const { addError, clearFlashes } = useFlash();
    const [ visible, setVisible ] = useState(false);

    useEffect(() => {
        clearFlashes('backups:create');
    }, [visible]);

    const submit = ({ name, ignored }: Values, { setSubmitting }: FormikHelpers<Values>) => {
        clearFlashes('backups:create')
        createServerBackup(uuid, name, ignored)
            .then(backup => {
                onBackupGenerated(backup);
                setVisible(false);
            })
            .catch(error => {
                console.error(error);
                addError({ key: 'backups:create', message: httpErrorToHuman(error) });
                setSubmitting(false);
            });
    };

    return (
        <>
            {visible &&
            <Formik
                onSubmit={submit}
                initialValues={{ name: '', ignored: '' }}
                validationSchema={object().shape({
                    name: string().required()
                        .matches(/^[w\][\w\s_.-]*[\w]$/, 'Backup name must only contain alpha-numeric characters, spaces, underscores, dashes, and periods. The name must start and end with an alpha-numeric character.')
                        .max(255),
                    ignored: string(),
                })}
            >
                <ModalContent
                    appear={true}
                    visible={visible}
                    onDismissed={() => setVisible(false)}
                />
            </Formik>
            }
            <button
                className={'btn btn-primary btn-sm'}
                onClick={() => setVisible(true)}
            >
                Create backup
            </button>
        </>
    );
};
