import { getMeta, getTemplate, pgClients, handlebarsSync } from '@opengis/fastify-table/utils.js';

const isValidDate = (dateStr) => (new Date(dateStr)).toString() !== 'Invalid Date';

export default async function calendarData({
    pg = pgClients.client, params = {}, query = {}, user = {},
}) {
    const { name } = params;
    const { granularity = 'month' } = query;

    if (!name) {
        return { message: 'not enough params: name', status: 400 };
    }

    if (query.granularity && !['day', 'month', 'week', 'year'].includes(granularity)) {
        return { message: 'invalid query params: granularity', status: 400 };
    }

    const body = await getTemplate('calendar', name);

    if (!body) {
        return { message: `calendar not found: ${name}`, status: 404 };
    }

    const {
        title,
        table,
        meta = {},
    } = body || {};

    if (!table) {
        return { message: 'not enough calendar params: table', status: 404 };
    }
    if (!pg.pk?.[table]) {
        return { message: `table pkey not found: ${table}`, status: 404 };
    }

    const filterList = (body?.filter || [])
        .concat(body?.filterInline || [])
        .concat(body?.filterCustom || [])
        .concat(body?.filterState || [])
        .concat(body?.filterList || [])
        .concat(body?.filters || [])
        .concat(body?.filter_list || []);

    const { columns = [] } = await getMeta({ pg, table });
    const columnList = columns?.map((el) => el?.name);

    const dateColumn = columns?.find((el) => el?.name === (meta?.date || meta?.start))?.name
        || columns?.find((el) => el?.name === 'cdate')?.name;

    if (query.date && !dateColumn) {
        return { message: 'invalid template params: date column not found', status: 400 };
    }

    if (query.date && !isValidDate(query.date)) {
        return { message: 'Invalid date: out of range or incorrect format', status: 400 };
    }

    const filterWhere = filterList?.length && query.filter?.length
        ? filterList
            .filter((el) => (Object.hasOwn(el, 'enabled') ? el?.enabled : true))
            .map((el) => {
                const val = query.filter.split(',').find((e) => e?.split('=')?.shift()?.includes(el.column || el.name))?.split('=')?.pop();
                if (val) return el.column && val ? `(${[`${el.column}::text='${val.replace(/'/g, "''")}'::text`, el.query].filter((el) => el).join(' and ')})` : el.query;
            })
            .filter((el) => el)
            .join(' and ')
        : undefined;

    const queryWhere = handlebarsSync.compile(body.query || '1=1')({ user, uid: user?.uid });
    const filterDate = query.date ? `date_trunc('${granularity}', "${dateColumn}"::date)='${query.date}'::date` : undefined;

    const where = [queryWhere, filterDate, filterWhere].filter((el) => el).join(' and ');

    const filtersByColumn = filterList.filter((el) => (Object.hasOwn(el, 'enabled') ? el?.enabled : true) && el?.column);

    const filters = [];
    if (filtersByColumn?.length) {
        await Promise.all(filtersByColumn.map(async (el) => {
            const { rows: filterData = [] } = await pg.query(`select ${el.column.replace(/'/g, "''")} as id, count(*) from ${table} 
            where ${el.query || '1=1'} and ${filterWhere || '1=1'} group by ${el.column.replace(/'/g, "''")}`);
            if (!filterData?.length) return;

            const clsData = await getTemplate(['cls', 'select'], el.cls);

            if (!el.cls || !clsData?.length) {
                filterData.forEach((item) => filters.push(item));
                return;
            }

            filterData.forEach((filter) => {
                const cls = clsData.find((item) => item.id === filter.id.toString());
                Object.assign(filter, { title: cls?.text, color: cls?.color });
                filters.push(filter);
            });
        }));
    }

    const filtersByPreparedQuery = filterList.filter((el) => (Object.hasOwn(el, 'enabled') ? el?.enabled : true) && !el?.column && el?.query);

    if (filtersByPreparedQuery?.length) {
        await Promise.all(filtersByPreparedQuery.map(async (el) => {
            const { rows = [] } = await pg.query(`select ${el.query} as id, count(*) from ${table} 
            where ${filterWhere || '1=1'} group by ${el.query}`);
            rows.forEach((item) => filters.push(item));
        }));
    }

    const metaColumns = Object.keys(meta)
        .filter((el) => ['date', 'start', 'end', 'title', 'status'].includes(el) && columnList.includes(meta[el]))
        .map((el) => `"${meta[el]}" as ${el}`);

    if (!metaColumns?.length) {
        return { message: `calendar param meta is invalid: invalid/empty keys`, status: 404 };
    }

    const q = `select ${metaColumns.join(',')} from ${table} where ${where}`;
    if (query?.sql && user?.user_type?.includes('admin')) return q;

    const { rows = [] } = await pg.query(q);

    return { title, filters, granularity, sql: user?.user_type?.includes('admin') ? q : undefined, rows };

}