/**
 * 
 * @author Ashique Mohammed
 * @description Generic Form create accepts an array of fields to update any resource
 * @version
 * 
 */

// let forms = [{
//     type: 'input'
// }, {
//     type: 'tabs',
//     tabs: [{}]
// }]

import React, { useState } from "react"

import moment from 'moment';

import Button from "../../../../../lib/elements/basic/button/button";

import {  Form, Input, Radio, InputNumber, DatePicker, TimePicker, Checkbox, Select, Row, Col, Tabs } from 'antd';

// import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';

import FileUpload from './../../../../components/file-upload/file-upload';

import Camera from './../../../../components/camera/camera';

// import { ColumnHeightOutlined } from '@ant-design/icons';

import { DateUtils } from "../../../../utils";

import { FieldCustomizer, TabCustomizer } from "./../../../../";

import { prepareAndExecuteScript } from './../../../../utils/script.utils';

const { TextArea } = Input;

const { Option } = Select;

const { TabPane } = Tabs;

// const grid = 4;


import './form-creator.scss'

/**
 * Component accepts fields to render the necessary components and returns the values on submission
 * 
 * @param {*} param0 
 * @returns 
 */
function FormCreator({
    /**If we want to change the existing information first we need the current information ie, selectedInformation*/
    selectedInformation,
    model,
    formContent = {},
    onSubmit,
    fields = [],
    callback,
    layout,
    // Below are arguments for use in form display
    onFieldUpdate,
    // onListUpdate,
    onFieldRemove,
}) {
    const [form] = Form.useForm();

    let layoutValue;

    if (layout) {
        layoutValue = layout
    } else {
        layoutValue = {
            labelCol: { span: 12 },
            wrapperCol: { span: 12 },
        };
    }

    // Variable used for loading state
    const [loading, setLoading] = useState(false);

    // Variable stores the form body values
    const [content, setContent] = useState(() => {

        // Body for the files
        let body = formContent || { [model.name]: {} };

        fields.forEach((entry) => {

            if (entry.transform) {

                body[entry.field] = entry.transform(body[entry.field]);
            }

            // If its a date
            if (['date'].indexOf(entry.type) !== -1 && formContent[entry.field]) {

                body[entry.field] = DateUtils.getMomentObject(formContent[entry.field]);
            }

        })

        return body;

    });

    /**
     * Function handles trigger of onupload
     * 
     * @param {*} element 
     * @param {*} attachments 
     */
    function onUpload(element, attachments) {
        form.setFieldsValue({ [element.field]: attachments });
    }

    /**
     * Custom Listener for onchange of input 
     * 
     * @param {*} element 
     * @param {*} attachments 
     */
    function onChange(element, value) {

        // Though we initially gave less respect to this method
        // This is the game changer - 29/01/23

        // Being able to listen to script , Bind onChange listeners to each input 
        // can avoid the need for building custom forms again 

        // console.log(element);

        /** */
        if (element.field) {

            form.setFieldsValue({ [element.field]: value });
        }
    }

    // const onDragEnd = (result) => {

    //     onListUpdate(result.source, result.destination);
    // }

    // const getListStyle = (isDraggingOver) => ({
    //     // background: isDraggingOver ? 'lightblue' : '',
    //     boxShadow: isDraggingOver ? '0px 0px 5px 5px #eeeeee' : '',

    //     // padding: grid,
    //     // width: 250
    // });

    /**
     * Function triggered for any onChange of input
     * 
     * @param {*} formFields 
     */
    const onFieldsChange = (formFields) => {

        formFields.forEach((field) => {
            if (field.name[0].includes('date')) {

                // values[field.field] = new Timestamp(moment(field.value).valueOf());

                // content[field.name[0]] = moment(field.value).format('DD/MM/YYYY');
            } else {
                // content[field.name[0]] = field.value;
            }
        })
        // setContent({ ...content })
    }

    /**
     * Values Change
     * 
     * @param {*} field 
     * @param {*} values 
     */
    const onValuesChange = async (field, values) => {

        // Find the input that is changed
        let key = Object.keys(field)[0];

        // Configuration
        let fieldConfiguration = fields.filter((config) => config.field === key)[0];

        // We have to check if there is an onChange script for the field
        if (fieldConfiguration.on_change) {

            // Before Submit of form , execute the script
            values = await prepareAndExecuteScript(values, null, fieldConfiguration.on_change);

        }
    }

    return (
        <section className="form-creator">
            <div>
                {/* <DragDropContext onDragEnd={onDragEnd}>
                <Droppable droppableId="droppable">

                    {(provided, snapshot) => (
                        <div
                            ref={provided.innerRef}
                            style={getListStyle(snapshot.isDraggingOver)}
                            {...provided.droppableProps}
                        > */}

                <Form
                
                    form={form}
                    // layout="inline"
                    {...layoutValue}
                    className="new-record"
                    name="new-record"
                    onFinish={(values) => {

                        setLoading(true);

                        // Do a screening to check if date fields are 
                        fields.forEach((field) => {

                            if (field.field && field.field.includes('date')) {

                                // values[field.field] = new Timestamp(new Date());

                                values[field.field] = moment(values[field.field]).valueOf();

                            } else {

                            }

                            if (field.type === ('time')) {

                                // values[field.field] = new Timestamp(new Date());

                                values[field.field] = moment(values[field.field]).format('HH:mm A');

                            } else {

                            }
                        })

                        onSubmit(values).then(() => {

                            setLoading(false);

                        });

                    }}
                    onFieldsChange={onFieldsChange}

                    onValuesChange={onValuesChange}
                    layout={layoutValue}
                    // validateMessages={validateMessages} 
                    initialValues={{
                        ...content,
                        remarks: ''
                    }}
                >

                    {/* Mapper maps each fields to build the form */}
                    <FieldMapper
                        fields={fields}
                        onChange={onChange}
                        selectedInformation={selectedInformation}
                        onUpload={onUpload}
                        onFieldUpdate={onFieldUpdate}
                        onFieldRemove={onFieldRemove}
                    />

                    {/* Mapper Ends */}

                    <Button loading={loading} type="primary" htmlType="submit" className="submit-button">
                        SUBMIT
                    </Button>
                </Form>
            </div>
            {/* </div>)}

                </Droppable>
            </DragDropContext> */}

        </section>
    );
}

export default FormCreator;


/**
 * Field Mapper accepts fields to build the form
 * 
 * @param {*} param0 
 * @returns 
 */
function FieldMapper({
    fields = [],
    onChange,
    selectedInformation,
    onUpload,
    onFieldUpdate,
    onFieldRemove
}) {

    return (<>

        {
            fields.map((field, index) => {

                // For Tabs
                if (field.type === 'Tabs') {

                    return (<Tabs defaultActiveKey="0">

                        {
                            field.tabs.map((tab, tabIndex) => {

                                return (
                                    <TabPane tab={`${tab.caption}`} key={tabIndex} >

                                        {/* Customizes the Tab */}
                                        <TabCustomizer

                                            tab={tab}
                                            field={field}

                                            onFieldUpdate={onFieldUpdate}
                                            fieldIndex={tabIndex}
                                            index={index}
                                            onFieldRemove={onFieldRemove} />
                                        {/* Customizes the Tab Ends */}

                                        {/* Mapper maps each fields to build the form */}
                                        <FieldMapper
                                            fields={tab.fields}
                                            onChange={onChange}
                                            onUpload={onUpload}
                                            onFieldUpdate={onFieldUpdate}
                                            onFieldRemove={onFieldRemove}
                                        />

                                    </TabPane>
                                )
                            })
                        }

                    </Tabs>)

                } else {

                    if (field.condition) {

                        return field.condition(content)
                            ?
                            <UserInput
                                onChange={onChange}
                                index={index}
                                key={index}
                                onUpload={onUpload}
                                field={field}
                                onFieldUpdate={onFieldUpdate}
                                onFieldRemove={onFieldRemove}
                            />
                            :
                            null

                    } else {
                        return <UserInput
                            onChange={onChange}
                            key={index}
                            selectedInformation={selectedInformation}
                            index={index}
                            field={field}
                            onUpload={onUpload}
                            onFieldUpdate={onFieldUpdate}

                            onFieldRemove={onFieldRemove}

                        />;
                    }
                }
            })
        }

    </>)

}



/**
 * Component accepts user input according to type
 * 
 * @param {*} param0 
 */
function UserInput({ field, onUpload, selectedInformation, onChange, onFieldUpdate, onFieldRemove, index }) {

    let props = {};

    // The extra text
    if (field.extra) {
        props.extra = field.extra;
    }

    const isVisible = field.visible !== undefined ? field.visible : true;

    /**
    * According to the type render each element
    * 
    * @param {*} field 
    */
    const inputElement = (field, onChange) => {

        if (field.visible) {

            if (field.visible === false) return null;

        }

        switch (field.type) {

            case 'number':
                return <InputNumber required={field.required} />

            case 'input':
                return <Input required={field.required} />

            case 'radio':
                return (
                    <Radio.Group>

                        {field.options.map((option, key) => <Radio key={key} value={option}>{option ? 'Yes' : 'No'}</Radio>)}
                        
                    </Radio.Group>

                );

            case 'checkbox':
                return <Checkbox.Group style={{ width: '100%' }}>
                    <Col>
                        {
                            field.options.map((option, key) => {

                                let opt = typeof option === 'string' ? option : option.value;

                                return <Row><Checkbox key={key} value={opt}>{opt}</Checkbox></Row>
                            })
                        }
                    </Col>
                </Checkbox.Group>

            // case 'checkbox':
            //     return <Checkbox.Group options={field.options}/>
            case 'textarea':
                return <TextArea rows={4} required={field.required} />

            case 'boolean':
                return <Select style={{ width: 120 }} required={field.required}>
                    {[true, false].map((option, key) => <Option key={key} value={option}>{option ? 'Yes' : 'No'}</Option>)}
                </Select>

            case 'select':
                // Handle static select options
                if (Array.isArray(field.options)) {

                    let defaultValue = field.default ? field.default : '';

                    /**If there is selected Information ie, for change info if there is selected guest information
                     * then we will match and set a default value from the guest details
                     */
                    if (selectedInformation) {
                        // Get the field value dynamically from selectedInformation[0] using field.field
                        const fieldValue = selectedInformation[0] && selectedInformation[0][field.caption];

                        // Find the default value based on field value and matching DisplayMember or description
                        const defaultOption = field.options.find(
                            (option) =>
                                option.displayMember?.toLowerCase() === fieldValue?.toLowerCase()
                        )

                        // If a matching option is found, set the default value; otherwise, do not set a default value (i.e., null or undefined)
                        defaultValue = defaultOption ? defaultOption.valueMember : null;

                    }
                    /**In case of default selection we need to call the onchange manually */
                    if (defaultValue) {

                        onChange(field, defaultValue);
                    }

                    return (
                        <Select defaultValue={defaultValue}
                            required={field.required}
                            style={{ width: 120 }}
                            onChange={(value) => onChange(field, value)}
                        >
                            {field.options.map((option, key) => (
                                <Option key={key} value={option.valueMember}>
                                    {option.displayMember}
                                </Option>
                            ))}
                        </Select>
                    );
                }
            // case 'checkbox':
            //     return <Checkbox.Group options={field.options} />

            case 'datetime':
                return <DatePicker format={"DD/MM/YYYY"} onChange={(record) => {

                    onChange(field, record.format('DD/MM/YYYY'))

                }} />

            case 'date':
                return <DatePicker format={"DD/MM/YYYY"} />


            case 'time':
                return <TimePicker format={"HH:mm A"} />

            // onChange={(record) => {

            //     console.log(record);

            //     onChange(field, record.format('HH:mm A'))

            // }}


            case 'upload':
                return <FileUpload multiple={field.multiple} maxSize={field.maxSize || 3} callback={(attachment) => onUpload(field, attachment)} onProgress={() => { }} />

            case 'camera':
                return <Camera />


            default:
                return <Input />
        }
    }

    // const getItemStyle = (draggableStyle, isDragging) => ({
    //     // some basic styles to make the items look a bit nicer
    //     userSelect: 'none',
    //     // padding: grid,
    //     // margin: `0 0 ${grid}px 0`,
    //     // position: `absolute`,
    //     // right: '40px',

    //     // change background colour if dragging
    //     // background: isDragging ? 'lightgreen' : 'whitesmoke',

    //     boxShadow: isDragging ? '0px 0px 5px 5px #eeeeee' : '',

    //     // styles we need to apply on draggables
    //     ...draggableStyle
    // });


    return (
        <>
            {/* <Draggable
                key={`${field.caption}-${index}`}
                draggableId={`${field.caption}-${index}`}
                index={index}
            >
                {(provided, snapshot) => (
                    <div>
                        <div

                            ref={provided.innerRef}
                            {...provided.dragHandleProps}
                            {...provided.draggableProps}
                            style={getItemStyle(
                                provided.draggableProps.style,
                                snapshot.isDragging
                            )}

                        > */}
            {isVisible === true ? (
                <div className="form-item-element">

                    {/* <Button
                                size="small"
                                ref={provided.innerRef}
                                {...provided.dragHandleProps}
                                {...provided.draggableProps}
                                style={getItemStyle(
                                    provided.draggableProps.style,
                                    snapshot.isDragging
                                )}
                            >

                                <ColumnHeightOutlined />

                            </Button> */}


                    {/* Customizes the form */}
                    <FieldCustomizer field={field} onFieldUpdate={onFieldUpdate} index={index} onFieldRemove={onFieldRemove} />
                    {/* Customizes the form Ends */}

                    <Form.Item {...props} name={field.field} label={field.caption} rules={[{ required: field.required, message: field.placeholder || 'Please enter ' + field.caption }]}>

                        {inputElement(field, onChange)}
                        {/* <InputElement field={field} /> */}

                    </Form.Item>

                </div>
            ) : null}
            {/* </div>
                    </div>
                )}
            </Draggable> */}

        </>
    )
}
