/**
 * @since Last modified: 2024/04/20 14:46:11
 * @constant formTools
 * @description Collection of operation methods for forms.
 * @see {@link https://codepen.io/axui/pen/abXwOXm|demo @ codepen}
 */
'use strict';
import ax from "../ax";
import getEl from "./getEl";
import fieldTools from "./fieldTools";
import Message from '../modules/Message';
import ajax from './ajax';
import isEmpty from "./isEmpty";
import validTools from "./validTools";
import unique from "./unique";
const formTools = {
    /**
     * @memberof form
     * @description Correct the name of the output value.
     * @param {getEl} item - The item of field,that contains name , type and value properties.
     * @returns {string} - Return a new name.
     * @see {@link https://codepen.io/axui/pen/XWGNgwN|demo @ codepen}
     * @example
     * ax.formTools.getHosts('#demo');
     * <!--return array-->
     */
    fixName: (item) => {
        let types = ['file', 'select-multiple', 'checkbox'];
        return (types.includes(item.type) && !item.name.endsWith('[]')) ? `${item.name}[]` : item.name;
    },
    /**
     * @memberof formTools
     * @description Based on the name attribute, obtain the host form field from the form.
     * @param {getEl} el - Form node.
     * @returns {null} - Return a array of host fields.
     * @see {@link https://codepen.io/axui/pen/XWGNgwN|demo @ codepen}
     * @example
     * ax.formTools.getHosts('#demo');
     * <!--return array-->
     */
    getHosts: (el) => {
        let condition = (k) => k.name && !k.name.includes(ax.nameSfx);
        return unique([...el?.querySelectorAll('input:not([type="submit"],[type="reset"]),select,textarea')]?.filter(condition), 'name');
    },
    /**
     * @memberof formTools
     * @description Clear the value of the form to zero.
     * @param {getEl} el - Form node.
     * @param {boolean} [zero=false] - Whether to return to zero directly.
     * @param {function} [cb] - The call back of reset.No paramsters.
     * @returns {null} - Return null.
     * @see {@link https://codepen.io/axui/pen/XWGNgwN|demo @ codepen}
     * @example
     * ax.formTools.reset('#demo',true);
     * <!--return null-->
     */
    reset: function (el, zero = false, cb) {
        let parentDom = getEl(el), fields = this.getHosts(parentDom);
        if (fields.length > 0) {
            for (let k of fields) {
                fieldTools.reset({ el: k, parent: parentDom, zero });
            }
            cb && cb();
        }
    },
    /**
     * @memberof formTools
     * @description Update form based on data, type=file is invalid.
     * @param {getEl} el - Form node.
     * @param {object} [data={}] - The data of updated.If it is a null value, use the reset method.
     * @param {function} [cb] - The call back of update.No paramsters.
     * @returns {null} - Return null.
     * @see {@link https://codepen.io/axui/pen/XWGNgwN|demo @ codepen}
     * @example
     * ax.formTools.setVals('#demo');
     * <!--return null-->
     */
    setVals: function (el, data = {}, cb) {
        let parentDom = getEl(el), fields = this.getHosts(parentDom);
        if (!parent || fields.length === 0) {
            return;
        }
        if (isEmpty(data)) {
            this.reset(parent, true);
        }
        for (let [k, val] of Object.entries(data)) {
            let item = fields.find((i) => i.name === k);
            item ? fieldTools.setVals({ el: item, val, parent }) : null;
        }
        cb && cb();
    },
    /**
     * @memberof formTools
     * @description Serialize a form and get the values of all form fields.
     * @param {object} obj  - Parameters.
     * @param {getEl|getEls} obj.el - Form dom or inputs array.
     * @param {"dft"|"string"|"formdata"|"json"} [obj.format='dft'] - Output value format (array)."string"=>`?name=wang&age=18&like[]=game&name[]=boll`;"formdata"=>return formData format;""=>array data;"json"=>{name:'',age:''...}.
     * @returns  {*} - The output values.
     * @see {@link https://codepen.io/axui/pen/yLwYwdE|demo @ codepen}
     * @example
     * ax.formTools.getVals({el:'#demo'});
     * <!--Return *-->
     */
    getVals: function (el, format = '') {
        let parentDom = getEl(el), none = format === 'string' ? '' : format === 'json' ? {} : format === 'formdata' ? new FormData() : [], fields = [], items = [], fieldName = '', output;
        try {
            if (parentDom) {
                fields = this.getHosts(parentDom);
                items = fields.map((k) => {
                    return { name: k.name, type: k.type, value: fieldTools.getVals({ el: k, parent: parentDom }) };
                });
                if (format === 'string') {
                    output = '';
                    items.forEach((k, i) => {
                        fieldName = this.fixName(k);
                        let symbol = i === 0 ? '' : '&';
                        if (Array.isArray(k.value)) {
                            if (k.value.length > 0) {
                                k.value.forEach((x) => {
                                    output += symbol + fieldName + '=' + (k.type === 'file' ? x.name : x);
                                });
                            }
                            else {
                                output += symbol + fieldName + '=';
                            }
                        }
                        else {
                            output += symbol + fieldName + '=' + k.value;
                        }
                    });
                }
                else if (format === 'formdata') {
                    output = new FormData();
                    items.forEach(k => {
                        fieldName = this.fixName(k);
                        if (Array.isArray(k.value)) {
                            if (k.value.length > 0) {
                                k.value.forEach((x) => {
                                    output.append(fieldName, x);
                                });
                            }
                        }
                        else {
                            output.append(fieldName, k.value);
                        }
                    });
                }
                else if (format === 'json') {
                    output = {};
                    items.forEach(k => {
                        fieldName = this.fixName(k);
                        output[fieldName] = k.value;
                    });
                }
                else {
                    output = items;
                }
                return output;
            }
            else {
                return none;
            }
        }
        catch {
            return none;
        }
    },
    /**
    * @memberof formTools
    * @description Submit form.
    * @param {getEl} el  - The node of form.
    * @param {false|null|object} opt - The parameters.If it is an object containing the url property, the form will be submitted async by building formdata.
    * @param {string} opt.url - The url of async request.
    * @param {getEl} opt.rel - The related node.usually a button.
    * @param {'post' | 'get'} opt.type - The type of async request.
    * @param {number} opt.delay - The delay after success submit.
    * @param {'loading' | 'text' | ''} opt.buffer - The related node loading mode.
    * @param {function} opt.success - The call back of success.
    * @param {function} opt.before - The call back before success.
    * @param {function} opt.error - The call back of error.
    * @param {function} opt.opened - The call back of xhr opened.
    * @returns null.
    * @see {@link https://codepen.io/axui/pen/PoLbxJe|demo @ codepen}
    * @example
    * ax.formTools.submit('#demo');
    * <!--return null-->
    */
    submit: function (el, opt) {
        let formDom = getEl(el);
        if (!formDom) {
            return false;
        }
        if (opt && opt.hasOwnProperty('url')) {
            let { url = '', rel, type = 'post', delay = 1000, buffer = '', success, before, error, opened, } = opt;
            if (!url || !parent) {
                throw new Error('The request url and form node are required!');
            }
            let relDom = getEl(rel), relText = relDom ? relDom.innerHTML : '', resume = () => {
                formDom?.classList.remove(`${ax.pfx}ajax-overlay`);
                if (relDom) {
                    if (buffer === 'loading' || buffer === 'text') {
                        relDom.removeAttribute('inert');
                        relDom.innerHTML = relText;
                    }
                    else {
                        relDom.removeAttribute('cheking');
                    }
                }
            };
            if (formDom.classList.contains(`${ax.pfx}ajax-overlay`)) {
                return false;
            }
            return ajax({
                url,
                data: this.getVals(formDom, 'formdata'),
                type,
                opened: (response) => {
                    opened && opened(response);
                },
                before: (response) => {
                    formDom?.classList.add(`${ax.pfx}ajax-overlay`);
                    if (relDom) {
                        if (buffer === 'loading') {
                            relDom.innerHTML = response.content;
                            relDom.setAttribute('inert', '');
                        }
                        else if (buffer === 'text') {
                            relDom.innerHTML = ax.lang.ajax.submit.btn;
                            relDom.setAttribute('inert', '');
                        }
                        else {
                            relDom.setAttribute('checking', '');
                        }
                    }
                    before && before(response);
                },
                success: (response) => {
                    setTimeout(() => {
                        resume();
                        new Message({
                            content: ax.lang.ajax.submit.succ,
                            result: 'succ',
                            iconShow: true,
                        });
                        success && success(response);
                    }, delay);
                },
                error: (response) => {
                    resume();
                    new Message({
                        content: ax.lang.ajax.submit.fail,
                        result: 'error',
                        iconShow: true,
                    });
                    error && error(response);
                }
            });
        }
        else {
            let validItem = ax.valids.find((k) => k.parent === formDom);
            if (validItem) {
                validTools.listenSubmit({ item: formDom });
                formDom.dispatchEvent(new Event('submit'));
            }
            else {
                formDom.nodeName === 'form' ? formDom.submit() : null;
            }
        }
    }
};
export default formTools;
