import type { FormikHelpers } from 'formik';
import { Form, Formik } from 'formik';
import { useEffect, useState } from 'react';
import tw from 'twin.macro';
import { array, number, object, string } from 'yup';

import createAllocation from '@/api/admin/nodes/allocations/createAllocation';
import getAllocations from '@/api/admin/nodes/getAllocations';
import getAllocations2 from '@/api/admin/nodes/allocations/getAllocations';
import Button from '@/components/elements/Button';
import Field from '@/components/elements/Field';
import type { Option } from '@/components/elements/SelectField';
import SelectField from '@/components/elements/SelectField';

interface Values {
    ips: string[];
    ports: number[];
    alias: string;
}

const distinct = (value: any, index: any, self: any) => {
    return self.indexOf(value) === index;
};

function CreateAllocationForm({ nodeId }: { nodeId: number }) {
    const [ips, setIPs] = useState<Option[]>([]);
    const [ports] = useState<Option[]>([]);

    const { mutate } = getAllocations2(nodeId, ['server']);

    useEffect(() => {
        getAllocations(nodeId).then(allocations => {
            setIPs(
                allocations
                    .map(a => a.ip)
                    .filter(distinct)
                    .map(ip => {
                        return { value: ip, label: ip };
                    }),
            );
        });
    }, [nodeId]);

    const isValidIP = (inputValue: string): boolean => {
        // TODO: Better way of checking for a valid ip (and CIDR)
        return inputValue.match(/^([0-9a-f.:/]+)$/) !== null;
    };

    const isValidPort = (inputValue: string): boolean => {
        // TODO: Better way of checking for a valid port (and port range)
        return inputValue.match(/^([0-9-]+)$/) !== null;
    };

    const submit = ({ ips, ports, alias }: Values, { setSubmitting }: FormikHelpers<Values>) => {
        setSubmitting(false);

        ips.forEach(async ip => {
            const allocations = await createAllocation(nodeId, { ip, ports, alias }, ['server']);
            await mutate(data => ({ ...data!, items: { ...data!.items!, ...allocations } }));
        });
    };

    return (
        <Formik
            onSubmit={submit}
            initialValues={{
                ips: [] as string[],
                ports: [] as number[],
                alias: '',
            }}
            validationSchema={object().shape({
                ips: array(string()).min(1, 'You must select at least one ip address.'),
                ports: array(number()).min(1, 'You must select at least one port.'),
            })}
        >
            {({ isSubmitting, isValid }) => (
                <Form>
                    <SelectField
                        id={'ips'}
                        name={'ips'}
                        label={'IPs and CIDRs'}
                        options={ips}
                        isValidNewOption={isValidIP}
                        isMulti
                        isSearchable
                        isCreatable
                        css={tw`mb-6`}
                    />

                    <SelectField
                        id={'ports'}
                        name={'ports'}
                        label={'Ports'}
                        options={ports}
                        isValidNewOption={isValidPort}
                        isMulti
                        isSearchable
                        isCreatable
                    />

                    <div css={tw`mt-6`}>
                        <Field id={'alias'} name={'alias'} label={'Alias'} type={'text'} />
                    </div>

                    <div css={tw`w-full flex flex-row items-center mt-6`}>
                        <div css={tw`flex ml-auto`}>
                            <Button type={'submit'} disabled={isSubmitting || !isValid}>
                                Create Allocations
                            </Button>
                        </div>
                    </div>
                </Form>
            )}
        </Formik>
    );
}

export default CreateAllocationForm;