import { isEmpty } from "@/utils/lodash";

const keyMapping = {
    // 3 query key để phân loại
    Sql: 'Sql',
    Uri: 'Uri',
    Get: 'Get',
    Key: 'Key',
    Method: 'Method',
    Params: 'Params',
    Headers: 'Headers',
    Content: 'Content',
    Top: 'Top',
    Page: 'Page',
    With: 'With',
    Sort: 'Sort',
    TTL: 'TTL',
    Ids: 'Ids'
};

const SQL_MODEL = {
    Get: "",
    Top: 1,
    Page: 0,
    With: [],
    Sort: [],
    TTL: "",
    Key: "",
    Ids: [],
}

const REQUEST_MODEL = {
    TTL: "",
    Key: "",
    Uri: "",
    Method: "Get",
    Params: [],
    Headers: [],
    Content: [],
};

const CUSTOM_MODEL = {
    TTL: "",
    Key: "",
    Sql: "",
};

const regexParseString = (key: String) => {
    const pattern = `${key}\\[(.*?)\\]`;
    return new RegExp(pattern);
}

const regexParseStringColon = (key: String, suffix: string = ']') => {
    const pattern = `${key}:\\s*([^;\\]]+)`;
    return new RegExp(pattern);
}

const getObjectWithModel = (model: any, queryString: string) => {
    let resultObject: any = {};
    for (let [key] of Object.entries(model)) {
        let resultValue: any = null;
        const matchValue = queryString.match(regexParseString(key)) || '';
        let currentMatchValue: any = matchValue && matchValue[1];
        if (key === keyMapping.With) {
            const splitArrWithSemi = currentMatchValue.split(';').length > 0 ? currentMatchValue.split(';') : [currentMatchValue];
            resultValue = splitArrWithSemi.map((_: any) => {
                const parseColon = _.split(':');
                return {
                    key: parseColon[0],
                    value: parseColon[1],
                }
            }).filter((item: any) => item.value);
        } else if (key === keyMapping.Sort) {
            const splitArrWithComma = currentMatchValue.split(',').length > 0 ? currentMatchValue.split(',') : [currentMatchValue];
            resultValue = splitArrWithComma.map((_: any) => {
                const isPlus = _.includes('+')
                const isMinus = _.includes('-')
                const key = _.replaceAll('-', '').replaceAll('+', '');
                return {
                    key,
                    value: isPlus ? '+' : isMinus ? '-' : ''
                }
            }).filter((item: any) => item.value);
        } 
        else if ([keyMapping.Content, keyMapping.Headers, keyMapping.Params].includes(key)) {
            if(currentMatchValue) {
                const parsedObject = JSON.parse(currentMatchValue);
                resultValue = Object.entries(parsedObject).map(([key, value]) => ({ key, value }));
            }
        } 
        else {
            resultValue = currentMatchValue;
        }

        resultObject = {
            ...resultObject,
            [key]: resultValue
        }
    }
    return resultObject;
}

const ArrayToString = (array: any, key: string, prefix: string, suffix: string) => {
    if (!Array.isArray(array) || array.length === 0) {
        return '';
    }
    const toString = array.filter((item: any) => item.value).reduce((accumulator: any, currentObject: any, index: number) => {
        const parseString = currentObject.key + prefix + currentObject.value;
        return index === 0 ? accumulator + parseString : accumulator + suffix + parseString;
    }, "")
    return key + "[" + toString + "]";
}

const parseDataQueryFormString = (command: string) => {
    if (isEmpty(command)) return null;

    let resultObject: any = null;
    let typeQuery: any = null;

    if (command.includes(keyMapping.Sql)) {
        typeQuery = keyMapping.Sql
    } else if (command.includes(keyMapping.Uri)) {
        typeQuery = keyMapping.Uri
    } else if (command.includes(keyMapping.Get)) {
        typeQuery = keyMapping.Get
    }

    if (!typeQuery) return null;

    switch (typeQuery) {
        case keyMapping.Sql:
            resultObject = Object.assign(CUSTOM_MODEL, getObjectWithModel(CUSTOM_MODEL, command));
            break;
        case keyMapping.Uri:
            resultObject = Object.assign(REQUEST_MODEL, getObjectWithModel(REQUEST_MODEL, command));
            break;
        case keyMapping.Get:
            resultObject = Object.assign(SQL_MODEL, getObjectWithModel(SQL_MODEL, command));
            break;
        default:
            resultObject = {};
    }
    return {
        typeQuery,
        value: resultObject
    }
}

const parseDataQueryFormObject = (command: any) => {
    const resultString = Object.entries(command).reduce((accumulator, [key, value]) => {
        if (!value) return accumulator;
        let parseString: string = '';
        if (key === keyMapping.With) {
            parseString = ArrayToString(value, key, ':', ';');
        } else if (key === keyMapping.Sort) {
            parseString = ArrayToString(value, key, '', ',');
        } else if ([keyMapping.Content, keyMapping.Headers, keyMapping.Params].includes(key)) {
            const reduceArrayToObject = value.reduce((acc: any, { key, value }: any) => {
                acc[key.trim()] = value.trim();
                return acc;
            }, {});
            const formattedValue = Object.keys(reduceArrayToObject).length ? JSON.stringify(reduceArrayToObject) : "";
            parseString = formattedValue ? `${key}[${formattedValue}]` : ""
        }  
        else {
            if (typeof value === "string") {
                value = value.trim();
            } else {
                value = value < 0 ? 0 : value;
            }
            if(value) parseString = key + "[" + value + "]"
        }
        return parseString ? accumulator + " " + parseString : accumulator;
    }, "");
    return resultString;
}

const DEFAULT_QUERY_DROP = (key: string, id: any, TTL?: String) => {
    return `Get[${key}] With[Id:${id}]${TTL ? ` TTL[${TTL}]` : ''}`;
}

const COLLECTION_QUERY_DROP = (key: string, ids: any, TTL?: String) => {
    let arraySplit : any = null; 
    try {
        arraySplit = ids.split(',');
    } catch {
        arraySplit =  1;
    }
    return `Get[${key}]${`${arraySplit && arraySplit.length > 0 ? ` Top[${arraySplit?.length}]` : ` Top[1]`}`} With[Ids:${ids.toString()}]${TTL ? ` TTL[${TTL}]` : ''}`;
}

const COLLECTION_PAGING_QUERY_DROP = (key: string, params: { key: string; value: any; }, TTL?: string) => {
    return `Get[${key}] Top[20] With[${params.key}:${params.value}]${TTL ? ` TTL[${TTL}]` : ''}`;
};

const getValueStringWithKey = (queryString: string, key: string = keyMapping.Ids) => {
    const matchValue = queryString.match(regexParseString(key)) || '';
    let currentMatchValue : any = matchValue && matchValue[1]; 
    return currentMatchValue;
}

const getValueStringWithKeyAndColon = (queryString: string, key: string = keyMapping.Ids) => {
    const matchValue = queryString.match(regexParseStringColon(key)) || '';
    let currentMatchValue : any = matchValue && matchValue[1]; 
    return currentMatchValue;
}

const getInputValue = (inputValue: any, typeGet: 'OBJECT' | 'ARRAY') => {
    if (!inputValue) return null;
    
    let _inputValue : any = null;
    try {
        _inputValue = JSON.parse(inputValue)
    } catch {
        _inputValue = inputValue
    }
    
    let typeOfInputValue : any = null;  
    if (Array.isArray(_inputValue)) {
        typeOfInputValue = 'ARRAY';
    } else if (typeof _inputValue === 'object' && _inputValue !== null) {
        typeOfInputValue = 'OBJECT';
    } else {
        typeOfInputValue = null;
    }

    if (typeGet === 'OBJECT') {
        if (typeOfInputValue === 'OBJECT') return _inputValue;
        if (typeOfInputValue === 'ARRAY') return _inputValue && _inputValue[0];
    } else if (typeGet === 'ARRAY') {
        if (typeOfInputValue === 'OBJECT') return [{..._inputValue}];
        if (typeOfInputValue === 'ARRAY') return _inputValue;
    }
}

export {
    parseDataQueryFormString,
    parseDataQueryFormObject,
    getValueStringWithKey,
    getValueStringWithKeyAndColon,
    getInputValue,
    DEFAULT_QUERY_DROP,
    COLLECTION_QUERY_DROP,
    COLLECTION_PAGING_QUERY_DROP
}