import { Logger, ILogObj } from 'tslog';
import { validate } from 'uuid';

const log: Logger<ILogObj> = new Logger({ name: '[CommonHelper]', type: 'pretty' });

export default class CommonHelper {
    static randomInteger(min: number, max: number): number | null {
        if (min > 0 && max > 0) {
            return Math.floor(Math.random() * (max - min + 1)) + min;
        } else {
            log.error(new Error('Min & Max must be > 0'));
            return null
        }
    }

    static inArray(array: string[], keyword: string): boolean {
        for (let val of array) {
            if (val === keyword) {
                return true;
            }
        }

        return false;
    }

    static async sleep(seconds: number): Promise<void> {
        if (seconds > 0) {
            return new Promise((resolve) => setTimeout(resolve, seconds * 1000));
        } else {
            log.error(new Error('Second must be > 0'));
        }
    }

    static convertArrayToObject(object: any, key: string, val = ''): any {
        const ret: any = {};

        for (let i in object) {
            const d = object[i];

            if (val) {
                ret[d[key]] = d[val];
            } else {
                ret[d[key]] = d;
            }
        }

        return ret;
    }

    static countObject(obj: any): number {
        let c = 0;

        for (let s in obj) {
            c++;
        }

        return c;
    }

    static capitalizeFirstLetter(str: string): string {
        if (str) {
            return str.charAt(0).toUpperCase() + str.slice(1);
        }

        return '';
    }

    static handleFilter(param: { filter: any, col_any_eq: string[], col_any_like: string[], additional_where?: string }): any {
        const whereAttrPre = [];
        const whereVal: any = {};

        if (param.filter) {
            try {
                param.filter = JSON.parse(param.filter);

                let filter_any = false;
                let filter_any_val = '';

                for (let p in param.filter) {
                    let f = String(param.filter[p])
                    let fr = param.filter[p]

                    if (p != 'any') {
                        // Map property names to database column names
                        // TypeORM uses camelCase for property names, but database uses snake_case
                        // For foreign keys, we need to use the actual database column name
                        let columnName = p;
                        // If it contains underscore, it's already snake_case, use it with quotes for PostgreSQL
                        if (p.includes('_')) {
                            columnName = '"' + p + '"';
                        }

                        if (!validate(f)) {
                            let found = false;
                            if (fr['from']) {
                                whereAttrPre.push(columnName + " >= :" + p + '_from');
                                whereVal[p + '_from'] = fr['from'];
                                found = true;
                            }
                            if (fr['to']) {
                                whereAttrPre.push(columnName + " <= :" + p + '_to');
                                whereVal[p + '_to'] = fr['to'];
                                found = true;
                            }
                            if (fr['like']) {
                                whereAttrPre.push('LOWER(' + columnName + ') like :' + p + '_like');
                                whereVal[p + '_like'] = '%' + fr['like'].toLowerCase() + '%';;
                                found = true;
                            }

                            if (!found) {
                                whereAttrPre.push("LOWER(" + columnName + ") = :" + p);
                                f = f.toLowerCase();

                                whereVal[p] = f;
                            }
                        } else {//uuid only
                            whereAttrPre.push(columnName + " = :" + p);
                            whereVal[p] = f;
                        }
                    } else {
                        filter_any = true;
                        filter_any_val = f;
                    }
                }

                if (filter_any) {
                    let wtp = []
                    if (param.col_any_eq.length > 0) {
                        const eqr = [];
                        for (let c of param.col_any_eq) {
                            eqr.push('LOWER(' + c + ') = :filter_eq');
                        }
                        const eq = '(' + eqr.join(' or ') + ')';

                        wtp.push(eq);
                    }
                    if (param.col_any_like.length > 0) {
                        const liker = [];
                        for (let c of param.col_any_like) {
                            liker.push('LOWER(' + c + ') like :filter_like');
                        }

                        const like = '(' + liker.join(' or ') + ')';

                        wtp.push(like);
                    }

                    if (wtp.length > 0) {
                        whereAttrPre.push('(' + wtp.join(' or ') + ')');
                    }

                    if (param.col_any_eq.length > 0) {
                        whereVal['filter_eq'] = filter_any_val;
                    }
                    if (param.col_any_like.length > 0) {
                        whereVal['filter_like'] = '%' + filter_any_val + '%';
                    }
                }
            } catch (e: any) {
                let wtp = []
                if (param.col_any_eq.length > 0) {
                    const eqr = [];
                    for (let c of param.col_any_eq) {
                        eqr.push('LOWER(' + c + ') = :filter_eq');
                    }
                    const eq = '(' + eqr.join(' or ') + ')';

                    wtp.push(eq);
                }
                if (param.col_any_like.length > 0) {
                    const liker = [];
                    for (let c of param.col_any_like) {
                        liker.push('LOWER(' + c + ') like :filter_like');
                    }

                    const like = '(' + liker.join(' or ') + ')';

                    wtp.push(like);
                }

                if (wtp.length > 0) {
                    whereAttrPre.push('(' + wtp.join(' or ') + ')');
                }


                if (param.col_any_eq.length > 0) {
                    whereVal['filter_eq'] = param.filter.toLowerCase();
                }
                if (param.col_any_like.length > 0) {
                    whereVal['filter_like'] = '%' + param.filter + '%';
                }

            }
        }

        const whereAttr = whereAttrPre.join(' and ') + (whereAttrPre.length > 0 && param.additional_where && param.additional_where != '' ? ' and ' : '') + (param.additional_where ?? '');

        return { whereAttr, whereVal };
    }

    static handleQueryFilter(filter: { [x: string]: any }, tableAlias?: string): any {
        const whereAttr = [];
        const whereVal: any = {};

        for (let p in filter) {
            let f = Array.isArray(filter[p]) ? filter[p] : String(filter[p]);

            if (p !== "any") {
                const columnName = p.includes(".") ? p : (tableAlias ? `${tableAlias}.${p}` : p);

                if (!validate(f)) {
                    if (p.endsWith("_from") || p.endsWith("_to")) {
                        const baseField = p.replace(/_(from|to)$/, "");
                        const qualifiedBaseField = baseField.includes(".") ? baseField : (tableAlias ? `${tableAlias}.${baseField}` : baseField);
                        const fromKey = `${baseField}_from`;
                        const toKey = `${baseField}_to`;

                        if (p.endsWith("_to") && !filter[p].includes(" ")) {
                            filter[p] = `${filter[p]} 23:59:59`;
                            f = filter[p];
                        }

                        whereAttr.push(`${qualifiedBaseField} BETWEEN :_${fromKey} AND :_${toKey}`);
                    } else if (f === "null") {
                        whereAttr.push(`${columnName} IS NULL`);
                    } else if (typeof f === "string" && f.includes("%")) {
                        whereAttr.push(`${columnName} ILIKE :_${p}`);
                        whereVal[`_${p}`] = p;
                    } else if (Array.isArray(f)) {
                        whereAttr.push(`${columnName} IN (:..._${p})`);
                    } else {
                        whereAttr.push(`${columnName} = :_${p}`);
                    }
                } else {
                    whereAttr.push(`${columnName} = :_${p}`);
                }

                whereVal[`_${p}`] = f;
            }
        }

        return { whereAttr, whereVal };
    }
}