import React, { useContext, useState, useEffect } from 'react';
import tw from 'twin.macro';
import { Formik, Form, Field as FormikField, FieldProps } from 'formik';
import { object, string, number } from 'yup';
import Field from '@/components/elements/Field';
import { Server } from '@/api/server/getServer';
import { ServerContext } from '@/state/server';
import { Button } from '@/components/elements/button/index';
import { Dialog, DialogWrapperContext } from '@/components/elements/dialog';
import asDialog from '@/hoc/asDialog';
import FlashMessageRender from '@/components/FlashMessageRender';
import Switch from '@/components/elements/Switch';
import Can from '@/components/elements/Can';
import { faExternalLinkSquareAlt, faTrash } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
interface Values {
    name: string;
    description: string;
    cpu: string | number;
    memory: string | number;
    disk: string | number;
    swap: number;
    database_limit: string | number;
    backup_limit: string | number;
    allocation_limit: string | number;
    power_action: string;
}
interface Props {
    server: Server;
    remainingResources: {
        cpu: number;
        memory: number;
        disk: number;
        swap: number;
        databases: number;
        backups: number;
        allocations: number;
    };
    onSplit?: (values: any) => Promise<void>;
    onUpdate?: (values: any) => Promise<void>;
    onDelete?: (serverUuid: string) => Promise<void>;
    onOpenServer?: (serverId: string) => void | Promise<void>;
    isEdit?: boolean;
    editServer?: Server;
}
const SplitServerDialog = asDialog({
    title: 'Split Server',
})(({ server: targetServer, remainingResources, onSplit, onUpdate, onDelete, onOpenServer, isEdit, editServer }: Props) => {
    const socket = ServerContext.useStoreState((s) => s.socket.instance);
    const { close } = useContext(DialogWrapperContext);
    const masterServer = ServerContext.useStoreState((state) => state.server.data!);
    const [memoryRaw, setMemoryRaw] = useState<string>();
    const [diskRaw, setDiskRaw] = useState<string>();
    const calculateTotalResources = () => {
        if (!isEdit || !editServer) {
            return remainingResources;
        }
        const masterHasUnlimitedCpu = masterServer.limits.cpu === 0;
        const masterHasUnlimitedMemory = masterServer.limits.memory === 0;
        const masterHasUnlimitedDisk = masterServer.limits.disk === 0;
        return {
            cpu: masterHasUnlimitedCpu ? masterServer.limits.cpu : (remainingResources.cpu + editServer.limits.cpu),
            memory: masterHasUnlimitedMemory ? masterServer.limits.memory : (remainingResources.memory + editServer.limits.memory),
            disk: masterHasUnlimitedDisk ? masterServer.limits.disk : (remainingResources.disk + editServer.limits.disk),
            swap: remainingResources.swap + editServer.limits.swap,
            databases: remainingResources.databases + editServer.featureLimits.databases,
            backups: remainingResources.backups + editServer.featureLimits.backups,
            allocations: remainingResources.allocations + editServer.featureLimits.allocations,
        };
    };
    const totalResources = calculateTotalResources();
    const maxIfFinite = (v: number) => (v && v > 0 ? v : undefined);
    const formatResource = (mb: number) => {
        if (mb === 0) return '∞';
        if (mb < 0) return 'Insufficient';
        if (!mb) return '0 MB';
        return `${mb} MB`;
    };
    const validationSchema = object().shape({
        name: string().optional(),
        description: string().nullable(),
        cpu: number()
            .transform((value) => value === '' ? 0 : Number(value))
            .required()
            .min(10)
            .when([], (schema) => (maxIfFinite(totalResources.cpu) ? schema.max(totalResources.cpu) : schema)),
        memory: number()
            .required()
            .min(128)
            .when([], (schema) => (maxIfFinite(totalResources.memory) ? schema.max(totalResources.memory) : schema)),
        disk: number()
            .required()
            .min(256)
            .when([], (schema) => (maxIfFinite(totalResources.disk) ? schema.max(totalResources.disk) : schema)),
        swap: number()
            .required()
            .min(0)
            .when([], (schema) => (maxIfFinite(totalResources.swap) ? schema.max(totalResources.swap) : schema)),
        database_limit: number()
            .transform((value) => value === '' ? 0 : Number(value))
            .required()
            .min(0)
            .when([], (schema) => (maxIfFinite(totalResources.databases) ? schema.max(totalResources.databases) : schema)),
        backup_limit: number()
            .transform((value) => value === '' ? 0 : Number(value))
            .required()
            .min(0)
            .when([], (schema) => (maxIfFinite(totalResources.backups) ? schema.max(totalResources.backups) : schema)),
        allocation_limit: number()
            .transform((value) => value === '' ? 0 : Number(value))
            .required()
            .min(0)
            .when([], (schema) => (maxIfFinite(totalResources.allocations) ? schema.max(totalResources.allocations) : schema)),
        power_action: string().oneOf(['true', 'false']).default('false'),
        sync_subusers: string().oneOf(['true', 'false']).default('true'),
    });
    const handleSubmit = (values: Values, { setSubmitting }: { setSubmitting: (isSubmitting: boolean) => void }) => {
        const payload = { ...values } as any;
        if (!payload.name || payload.name.trim() === '') {
            payload.name = `${masterServer.name}'s child`;
        }
        if (totalResources.swap === 0) payload.swap = 0;
        if (isEdit && onUpdate) {
            onUpdate(payload)
                .then(() => {
                    if (values.power_action === 'true' && socket) {
                        socket.send('set state', 'restart');
                    }
                    close();
                })
                .catch(() => {
                    setSubmitting(false);
                });
        } else {
            onSplit && onSplit(payload)
                .then(() => {
                    if (values.power_action === 'true' && socket) {
                        socket.send('set state', 'restart');
                    }
                    close();
                })
                .catch(() => {
                    setSubmitting(false);
                });
        }
    };
    return (
        <Formik
            onSubmit={handleSubmit}
            initialValues={{
                name: isEdit && editServer ? editServer.name : '',
                description: isEdit && editServer ? editServer.description : '',
                cpu: isEdit && editServer ? editServer.limits.cpu : '' as string | number,
                memory: isEdit && editServer ? editServer.limits.memory : '' as string | number,
                disk: isEdit && editServer ? editServer.limits.disk : '' as string | number,
                swap: isEdit && editServer ? editServer.limits.swap : (remainingResources.swap === 0 ? 0 : 0),
                database_limit: isEdit && editServer ? editServer.featureLimits.databases : '' as string | number,
                backup_limit: isEdit && editServer ? editServer.featureLimits.backups : '' as string | number,
                allocation_limit: isEdit && editServer ? editServer.featureLimits.allocations : '' as string | number,
                power_action: 'false',
                sync_subusers: true,
            } as Values}
            validationSchema={validationSchema}
        >
            {({ isSubmitting, submitForm }) => (
                <>
                    <FlashMessageRender key={'server:split'} />
                    <Form css={tw`m-0`}>
                        <div css={tw`grid grid-cols-1 md:grid-cols-2 gap-4`}>
                            <Field name="name" label="Name" placeholder={`${masterServer.name}'s child`} />
                            <Field name="description" label="Description" placeholder="(Optional)" />
                            <Field name="cpu" label={`CPU (${totalResources.cpu > 0 ? totalResources.cpu + '%' : '∞'} Max)`} type="number" placeholder={totalResources.cpu.toString()} />
                            <Field name="memory" label={`MEMORY (${formatResource(totalResources.memory)} MAX)`} type="number" placeholder={totalResources.memory.toString()} />
                            <Field name="disk" label={`DISK (${formatResource(totalResources.disk)} MAX)`} type="number" placeholder={totalResources.disk.toString()} />
                            {totalResources.swap > 0 && (
                                <Field name="swap" label="Swap Limit (MB)" type="number" />
                            )}
                            <Field name="database_limit" label={`Database (${totalResources.databases} Max)`} type="number" placeholder={totalResources.databases.toString()} />
                            <Field name="backup_limit" label={`Backup (${totalResources.backups} Max)`} type="number" placeholder={totalResources.backups.toString()} />
                            <Field name="allocation_limit" label={`Allocation (${totalResources.allocations} Max)`} type="number" placeholder={totalResources.allocations.toString()} />
                        </div>
                        <div css={tw`mt-4 bg-neutral-700 border border-neutral-800 shadow-inner p-4 rounded`}>
                            <FormikField name="power_action">
                                {({ field, form }: FieldProps) => (
                                    <Switch
                                        name="power_action"
                                        label={`Restart master after ${isEdit ? 'update' : 'create'}`}
                                        description={`Automatically restart the master server after ${isEdit ? 'update' : 'create'}`}
                                        defaultChecked={false}
                                        readOnly={isSubmitting}
                                        onChange={(e) => {
                                            form.setFieldValue('power_action', e.target.checked ? 'true' : 'false');
                                        }}
                                    />
                                )}
                            </FormikField>
                        </div>
                        {!isEdit && (
                            <div css={tw`mt-4 bg-neutral-700 border border-neutral-800 shadow-inner p-4 rounded`}>
                                <FormikField name="sync_subusers">
                                    {({ field, form }: FieldProps) => (
                                        <Switch
                                            name="sync_subusers"
                                            label="Sync Subusers"
                                            description="Sync subusers from the master server to this server"
                                            defaultChecked={true}
                                            readOnly={isSubmitting}
                                            onChange={(e) => {
                                                form.setFieldValue('sync_subusers', e.target.checked ? 'true' : 'false');
                                            }}
                                        />
                                    )}
                                </FormikField>
                            </div>
                        )}
                    </Form>
                    <Dialog.Footer>
                        <Button.Text className={'w-full sm:w-auto'} onClick={close}>
                            Cancel
                        </Button.Text>
                        <Can action={'splitter.delete'}>
                            {isEdit && onDelete && (
                                <Button.Danger onClick={async () => {
                                    if (editServer && window.confirm('Are you sure you want to delete this server? This action cannot be undone.')) {
                                        await onDelete(editServer.uuid);
                                        close();
                                    }
                                }}>
                                    <FontAwesomeIcon icon={faTrash} className={'mr-1'} /> Delete
                                </Button.Danger>
                            )}
                        </Can>
                        {isEdit && onOpenServer && editServer && (
                            <Button onClick={() => window.open(`/server/${editServer.id}`, '_blank')}>
                                <FontAwesomeIcon icon={faExternalLinkSquareAlt} className={'mr-1'} /> Open
                            </Button>
                        )}
                        <Can action={'splitter.update'}>
                            <Button disabled={isSubmitting} onClick={submitForm}>
                                {isEdit ? 'Update' : 'Split'}
                            </Button>
                        </Can>
                    </Dialog.Footer>
                </>
            )}
        </Formik>
    );
});
export default SplitServerDialog;
