import { FieldArray, Formik } from 'formik';
import React, { useMemo } from 'react';
import * as Yup from 'yup';

import { IOptions } from 'lib/utils/react-select';
import { IUDFRendererValues, UDFEngineConfigsByLanguage } from 'lib/utils/udf';

import { Button, SoftButton } from 'ui/Button/Button';
import { IconButton } from 'ui/Button/IconButton';
import { SimpleField } from 'ui/FormikField/SimpleField';
import { Subtitle, Title } from 'ui/Title/Title';
import { Link } from 'ui/Link/Link';
import { Message } from 'ui/Message/Message';

import './UDFForm.scss';

interface IUDFFormProps {
    onConfirm: (udfScript: string) => void;
    engineLanguage: string;
}

const UDFFormValuesSchema = Yup.object().shape({
    functionName: Yup.string().min(1).required(),
    udfLanguage: Yup.string().min(1).required(),
    script: Yup.string().min(1).required(),
    // All other fields are optional
});

export const UDFForm: React.FC<IUDFFormProps> = ({
    onConfirm,
    engineLanguage,
}) => {
    const engineUDFConfig = UDFEngineConfigsByLanguage[engineLanguage];
    const languageOptions: IOptions<string> = useMemo(
        () =>
            engineUDFConfig.supportedUDFLanguages.map((languageConfig) => ({
                label: languageConfig.displayName ?? languageConfig.name,
                value: languageConfig.name,
            })),
        [engineUDFConfig]
    );

    const initialValues: IUDFRendererValues = useMemo(
        () => ({
            functionName: '',
            udfLanguage: languageOptions[0].value,
            outputType: '',
            parameters: [],
            script: '',
            ...engineUDFConfig.prefills,
        }),
        [languageOptions, engineUDFConfig]
    );

    return (
        <div className="UDFForm">
            <Formik
                validateOnMount
                initialValues={initialValues}
                onSubmit={(formValues: IUDFRendererValues) => {
                    onConfirm(engineUDFConfig.renderer(formValues));
                }}
                validationSchema={UDFFormValuesSchema}
            >
                {({ values, handleSubmit, isValid }) => {
                    const selectedLanguageConfig = engineUDFConfig.supportedUDFLanguages.find(
                        (l) => l.name === values.udfLanguage
                    );

                    const parametersDOM = selectedLanguageConfig?.noParameters ? null : (
                        <FieldArray
                            name="parameters"
                            render={(arrayHelper) => {
                                const parameterRowsDOM = values.parameters.map(
                                    (_, idx) => (
                                        <div key={idx} className="flex-row">
                                            <div className="flex4">
                                                <SimpleField
                                                    type="input"
                                                    name={`parameters[${idx}].name`}
                                                    label={() => null}
                                                />
                                            </div>
                                            <div className="flex4">
                                                <SimpleField
                                                    type="react-select"
                                                    label={() => null}
                                                    name={`parameters[${idx}].type`}
                                                    options={
                                                        engineUDFConfig.dataTypes
                                                    }
                                                    selectProps={{
                                                        placeholder:
                                                            'Select types',
                                                    }}
                                                    creatable
                                                />
                                            </div>

                                            <div className="flex1">
                                                <IconButton
                                                    icon="X"
                                                    onClick={() =>
                                                        arrayHelper.remove(idx)
                                                    }
                                                />
                                            </div>
                                        </div>
                                    )
                                );

                                return (
                                    <div className="UDFForm-parameters">
                                        <Title size="smedium">Parameters</Title>
                                        <div className="flex-row">
                                            <Subtitle className="flex4 ml16">
                                                Name
                                            </Subtitle>
                                            <Subtitle className="flex4 ml16">
                                                Type
                                            </Subtitle>
                                            <div className="flex1" />
                                        </div>

                                        {parameterRowsDOM}
                                        <div className="center-align">
                                            <SoftButton
                                                size="small"
                                                title="Add New Parameter"
                                                icon="Plus"
                                                onClick={() =>
                                                    arrayHelper.push({
                                                        name: '',
                                                        type: '',
                                                    })
                                                }
                                            />
                                        </div>
                                    </div>
                                );
                            }}
                        />
                    );

                    return (
                        <>
                            {engineUDFConfig.docs?.length > 0 && (
                                <Message type="tip">
                                    <div>
                                        <b>UDF Docs:</b>
                                    </div>
                                    {engineUDFConfig.docs.map((doc, idx) => (
                                        <Link key={idx} to={doc.url}>
                                            {doc.name ?? doc.url}
                                        </Link>
                                    ))}
                                </Message>
                            )}
                            <SimpleField
                                name="functionName"
                                type="input"
                                label="Function Name *"
                                stacked
                                required
                            />
                            <div className="flex-row">
                                <div className="flex1 mr4">
                                    <SimpleField
                                        type="react-select"
                                        name="udfLanguage"
                                        label="Language *"
                                        options={languageOptions}
                                        stacked
                                    />
                                </div>
                                <div className="flex1 ml4">
                                    {selectedLanguageConfig?.noOutputType ? null : (
                                        <SimpleField
                                            label="Output Type"
                                            stacked
                                            name="outputType"
                                            type="react-select"
                                            creatable
                                            options={engineUDFConfig.dataTypes}
                                            selectProps={{
                                                placeholder: 'Select types',
                                            }}
                                        />
                                    )}
                                </div>
                            </div>
                            {parametersDOM}
                            <SimpleField
                                type="code-editor"
                                name="script"
                                label="Code *"
                                mode={
                                    selectedLanguageConfig?.codeEditorMode ??
                                    'sql'
                                }
                                stacked
                                required
                            />

                            <div className="right-align">
                                <Button
                                    onClick={() => handleSubmit()}
                                    disabled={!isValid}
                                    title="Submit"
                                />
                            </div>
                        </>
                    );
                }}
            </Formik>
        </div>
    );
};