import { Option } from 'commander';
import * as DateFns from 'date-fns';
import { PrettyPrint } from '../output/pretty_print.js';

export class Metrics {

    constructor(program, logger, services) {
        this.logger = logger;
        this.services = services;
        services["metrics"] = this;

        this.logger.debug('Metrics.constructor');

        const metricscommand = program
            .command('metrics')
            .description('Get metrics on applications and API usage');

        metricscommand
            .command('applications')
            .description('Get metrics on applications')
            .addOption(new Option('--day <date>', 'get metrics for a specific day (YYYY-MM-DD)').conflicts(['month', 'year']))
            .addOption(new Option('--month <month>', 'get metrics for a specific month (YYYY-MM)').conflicts(['day', 'year']))
            .addOption(new Option('--year <year>', 'get metrics for a specific year (YYYY)').conflicts(['day', 'month']))
            .addOption(new Option('--period <period>', 'aggregation period (day, month). Forced to day for day metrics, defaults to day for month metrics, forced to month for year metrics'))
            .addOption(new Option('-g, --group <group>', 'get metrics for a group'))
            .addOption(new Option('-z, --dczones', 'get metrics for a DC zone'))
            .addOption(new Option('-j, --json', 'display raw JSON'))
            .action(async (options) => {
                PrettyPrint.withSpinner(
                    async () => { return await this.getApplicationsMetrics(options); },
                    (json) => { PrettyPrint.display(this.formatCPaaSMetricsResult(json, options), undefined, options.json); }
                );
            });

        metricscommand
            .command('apigroupsmapping')
            .description('Get the mapping of CPaaS metric groups')
            .action(async (options) => {
                PrettyPrint.withSpinner(
                    async () => { return await this.apiGroupsMapping(options); },
                    (json) => { PrettyPrint.display(json); }
                );
            });

        const metricsapplicationcommand = metricscommand
            .command('application')
            .argument('<id>', 'id of the application')
            .description('Get metrics for an application')
            .addOption(new Option('--day <date>', 'get metrics for a specific day (YYYY-MM-DD)').conflicts(['month', 'year']))
            .addOption(new Option('--month <month>', 'get metrics for a specific month (YYYY-MM)').conflicts(['day', 'year']))
            .addOption(new Option('--year <year>', 'get metrics for a specific year (YYYY)').conflicts(['day', 'month']))
            .addOption(new Option('--period <period>', 'aggregation period (day, month, hour). Forced to day for day metrics, defaults to day for month metrics, forced to month for year metrics'))
            .addOption(new Option('-g, --group <group>', 'get metrics for a group'))
            .addOption(new Option('-j, --json', 'display raw JSON'))
            .action(async (id, options) => {
                PrettyPrint.withSpinner(
                    async () => { return await this.getCPaaSMetrics(id, options); },
                    (json) => { PrettyPrint.display(this.formatCPaaSMetricsResult(json, options), undefined, options.json); }
                );
            });

        metricscommand
            .command('about')
            .description('Get metrics portal information')
            .addOption(new Option('-j, --json', 'display raw JSON'))
            .action(async (options) => {
                PrettyPrint.withSpinner(
                    async () => this.about(),
                    (json) => PrettyPrint.display(json, undefined, options.json)
                );
            });
    }

    async about() {
        this.logger.debug('Metrics.about');
        return this.services['rest'].restCall('GET', '/api/rainbow/metrics/v1.0/about');
    }


    async apiGroupsMapping(options) {
        this.logger.debug('Metrics.apiGroupsMapping');

        const json = await this.services["rest"].restCall('GET', '/api/rainbow/metrics/v1.0/cpaasmetrics/apiGroupsMapping');
        return json;
    }

    async getApplicationsMetrics(options) {
        this.logger.debug('Metrics.getApplicationsMetrics');

        const query_parameters = [];
        const now = Date.now();
        var fromDate = DateFns.startOfDay(now);
        var toDate = DateFns.endOfDay(now);
        try {

            if (options.day) {
                const day = Date.parse(options.day);
                fromDate = DateFns.startOfDay(day);
                toDate = DateFns.endOfDay(day);
                query_parameters.push(['period', 'day']);
            }
            if (options.month) {
                const month = Date.parse(options.month);
                fromDate = DateFns.startOfMonth(month);
                toDate = DateFns.endOfMonth(month);
                if (!options.period) {
                    query_parameters.push(['period', 'day']);
                }
                else {
                    query_parameters.push(['period', options.period]);
                }
            }
            if (options.year) {
                const year = Date.parse(options.year);
                fromDate = DateFns.startOfYear(year);
                toDate = DateFns.endOfYear(year);
                query_parameters.push(['period', 'month']);
            }
        }
        catch (error) {
            this.logger.error(error);
            throw error;
        }
        this.logger.debug(`fromDate: ${fromDate.toISOString()},  toDate: ${toDate.toISOString()}`);

        query_parameters.push(['fromDate', fromDate.toISOString()]);
        query_parameters.push(['toDate', toDate.toISOString()]);

        const json = await this.services["rest"].restCall('GET', '/api/rainbow/metrics/v1.0/applicationmetrics/applications', query_parameters);
        return json;
    }

    async getCPaaSMetrics(applicationId, options) {
        this.logger.debug('Metrics.getCPaaSMetrics');

        const query_parameters = [];
        const now = Date.now();
        var fromDate = DateFns.startOfDay(now);
        var toDate = DateFns.endOfDay(now);

        try {
            if (options.period) {
                query_parameters.push(['period', options.period]);
            }
            if (options.day) {
                const day = Date.parse(options.day);
                fromDate = DateFns.startOfDay(day);
                toDate = DateFns.endOfDay(day);
                query_parameters.push(['period', 'day']);
            }
            if (options.month) {
                const month = Date.parse(options.month);
                fromDate = DateFns.startOfMonth(month);
                toDate = DateFns.endOfMonth(month);
                if (!options.period) {
                    query_parameters.push(['period', 'day']);
                }
                else {
                    query_parameters.push(['period', options.period]);
                }
            }
            if (options.year) {
                const year = Date.parse(options.year);
                fromDate = DateFns.startOfYear(year);
                toDate = DateFns.endOfYear(year);
                query_parameters.push(['period', 'month']);
            }
        }
        catch (error) {
            this.logger.error(error);
            throw error;
        }

        query_parameters.push(['fromDate', fromDate.toISOString()]);
        query_parameters.push(['toDate', toDate.toISOString()]);

        const json = await this.services["rest"].restCall('GET', `/api/rainbow/metrics/v1.0/cpaasmetrics/${applicationId}`, query_parameters);
        return json;
    }

    // This is an example of the structure of the result for CPaaS metrics, which is quite complex and requires specific formatting to be displayed in a readable way. The formatCPaaSMetricsResult method is intended to handle this formatting, but it is currently not fully implemented and may need to be adapted based on the actual structure of the result returned by the API.
    // {
    //    "data": [
    //        {
    //            "groupCounters": [
    //                {
    //                    "group": "login",
    //                    "count": 16
    //                },
    //                {
    //                    "group": "users",
    //                    "count": 145
    //                },
    //                {
    //                    "group": "subscriptions",
    //                    "count": 7
    //                },
    //                {
    //                    "group": "administration",
    //                    "count": 39
    //                }
    //            ],
    //            "aggregationStartDate": "2017-10-25T14:00:00.000Z"
    //        },
    //        {
    //            "groupCounters": [
    //                {
    //                    "group": "login",
    //                    "count": 3
    //                }
    //            ],
    //            "aggregationStartDate": "2017-10-25T15:00:00.000Z"
    //        }
    //    ],
    //    "aggregationPeriod": "hour"
    // }
    // The formatCPaaSMetricsResult method should take the result of the getCPaaSMetrics method and return a JSON object 
    // that can be displayed in a table with PrettyPrint.display().
    // The returned JSON must be an object containing a data property which is an array of objects.
    // The table must display columns for the aggregationStartDate, each group entry and its count. For example, the columns could be: aggregationStartDate, login, users, subscriptions, administration. Each row of the table would then correspond to an entry in the data array, with the aggregationStartDate and the counts for each group.   
    formatCPaaSMetricsResult(result, options) {
        try {
            const formattedResult = { data: [] };
            const groupsSet = new Set();
            const entries = result?.data ?? [];

            // First pass to collect all group names
            for (const entry of entries) {
                for (const groupCounter of (entry.groupCounters ?? [])) {
                    groupsSet.add(groupCounter.group);
                }
            }

            const groups = Array.from(groupsSet);

            // Second pass to format the data
            for (const entry of entries) {
                const formattedEntry = { aggregationStartDate: entry.aggregationStartDate };
                const counters = entry.groupCounters ?? [];
                for (const group of groups) {
                    const groupCounter = counters.find(gc => gc.group === group);
                    formattedEntry[group] = groupCounter ? groupCounter.count : 0;
                }
                formattedResult.data.push(formattedEntry);
            }

            return formattedResult;
        }
        catch (error) {
            this.logger.error(error);
            return result; // Fallback to original result if formatting fails
        }
    }
}  