#!/usr/bin/env node

const packageJson = require('./package.json');
const fetch = require('node-fetch');
const colors = require('colors');
const os = require('os');
const fs = require('fs');
const getopts = require('getopts');

const error = `[ERROR]: `.bold;

console.log(
    `Grafana-Find (${packageJson.description})`.yellow.bold,
    `v${packageJson.version}`.cyan.bold
);

const usage = {
    description: 'Utility to find strings in dashboards',
    params: {
        user: 'Grafana username',
        version: 'Display the version number and exit',
        help: 'Display this help message'
    }
};

const opts = getopts(process.argv.slice(2), {
    alias: {
        version: 'v',
        help: 'h'
    },
    boolean: [
        'version',
        'help'
    ]
});

if (opts.version) {
    process.exit();
}
if (opts.help) {
    console.log(`Usage:`.gray, `grafana-find`, `<string to search>`.magenta);
    process.exit();
}

if (!opts._[0]) {
    console.error(`${error}The string to search for is missing`.red);
    process.exit(1);
}
if (opts._.length > 1) {
    console.error(`${error}This command doesn't allow more parameters`.red);
    process.exit(1);
}

let config;
const configPaths = [
    os.homedir(),
    `${__dirname}`
];
for (const configPath of configPaths) {
    const configFile = `${configPath}/.grafana-find.json`;
    if (fs.existsSync(configFile)) {
        config = require(configFile);
        break;
    }
}
if (!config) {
    console.error(`${error}Configuration file not found, search paths: .grafana-find.json: ${configPaths.join(':')}\n`.red);
    process.exit(1);
}

const findAll = opts._[0];
const grafanaUrl = config.grafanaUrl;
let user = config.user;
let passw = config.password;

const grafanaApi = `${grafanaUrl}/api`;
const urlOrganizations = `${grafanaUrl}/api/orgs`;
const urlDashboards = `${grafanaApi}/search?orgId=`;
const urlUID = `${grafanaApi}/dashboards/uid/`;
let numberOfDashboards = 0;
let numberOfPanels = 0;
let numberOfVariables = 0;
let numberOfObjects = 0;
let titlePanels = new Array;
let nameVariables = new Array;

const regexRawSQL = new RegExp(findAll, 'i');

async function main(){
    if (!user) {
        const readline = require('readline');
        const rl = readline.createInterface({
            input: process.stdin,
            output: process.stdout
        });

        const answer = await new Promise(resolve => {
            rl.question(colors.green('Enter your user: '), resolve);
        });
        user = `${answer}`;
        if (!answer) {
            console.error(`\n${error}You need to put a user\n`.red);
            process.exit(0);
        }
        rl.close();
    }

    if (!passw) {
        const readline = require('readline');
        const rl = readline.createInterface({
            input: process.stdin,
            output: process.stdout
        });

        rl.stdoutMuted = true;

        const answer = await new Promise(resolve => {
            rl.question(colors.green('Enter your password: '), resolve);
            rl._writeToOutput = function _writeToOutput(stringToWrite) {
                if (rl.stdoutMuted)
                rl.output.write("*");
                else
                rl.output.write(stringToWrite);
            };
        });
        passw = `${answer}`;
        if (!answer) {
            console.error(`\n${error}You need to put a password\n`.red);
            process.exit(0);
        }
        rl.close();
    }

    const credentials = `Basic ` + Buffer.from(`${user}:${passw}`).toString('base64');

    let response = await fetch(urlOrganizations, {
        method: "GET",
        headers: {
            Authorization: credentials
        }
    });

    let AllOrganizations = await response.json();

    if (AllOrganizations.message==='invalid username or password') {
        console.error(`\n${error}Invalid username or password\n`.red);
        process.exit(1);
    }

    console.clear();
    console.log(
        `--------- Grafana-Find (${packageJson.description})`.yellow.bold,
        `v${packageJson.version}`.cyan.bold,
        `--------`.yellow.bold,
    );
    console.log(colors.green.bold(`-------------------- Starting process --------------------\n`));

    for (let x in AllOrganizations) {

        console.log(colors.gray.bold(`(Organization: ${AllOrganizations[x].name})\n`));
        response = await fetch(`${urlDashboards}${AllOrganizations[x].id}`, {
            method: "GET",
            headers: {
                Authorization: credentials
            },
            redirect: 'manual'
        });

        if (response.status === 302) {
            response = await fetch(`${urlDashboards}${AllOrganizations[x].id}`, {
                method: 'GET',
                headers: {
                    Accept: 'application/json'
                },
                redirect: 'manual'
            });
        }

        let allUID = await response.json();

        for (let i in allUID) {
            let url = `${urlUID}${allUID[i].uid}`;
            response = await fetch(url, {
                method: "GET",
                headers: {
                    Authorization: credentials,
                },
                redirect: 'manual'
            });

            if (response.status === 404) {
                response = await fetch(url, {
                    method: 'GET',
                    headers: {
                        Accept: 'application/json'
                    },
                    redirect: 'manual'
                });
            }

            let data = await response.json();

            let isFound = false;
            let isFoundSomething = false;

            const dashboard = data.dashboard;
            if (dashboard) {
                if (dashboard.panels)
                for (const panel of dashboard.panels) {
                    if (panel.targets)
                    for (const target of panel.targets) {
                        isFound = regexRawSQL.test(target.rawSql);
                        if (isFound) {
                            if (panel.title)
                                titlePanels.push(panel.title);
                            else
                                titlePanels.push(`null`);
                            numberOfPanels++;
                            isFoundSomething=true;
                        }
                    }
                }

                if (dashboard.templating)
                for (const list of dashboard.templating.list) {
                    isFound = regexRawSQL.test(list.query);
                    if (isFound) {
                        nameVariables.push(list.name)
                        numberOfVariables++;
                        isFoundSomething=true;
                    }
                }
            }

            if (isFoundSomething) {
                const linkUrl = `${grafanaUrl}/d/${allUID[i].uid}?orgId=${AllOrganizations[x].id}`;
                console.log((linkUrl).yellow.underline, dashboard.title);
                if (numberOfPanels) {
                    console.log(colors.cyan.bold(`> ${numberOfPanels} panels`));
                    console.log(colors.cyan(titlePanels.toString().split(",")));
                }
                if (numberOfVariables) {
                    console.log(colors.magenta.bold(`> ${numberOfVariables} variables`));
                    console.log(colors.magenta(nameVariables.toString().split(",")));
                }
                console.log('')
                numberOfDashboards++;
            }

            titlePanels = [];
            nameVariables= [];
            numberOfObjects = numberOfPanels + numberOfVariables + numberOfObjects
            numberOfPanels=0;
            numberOfVariables=0;
        }

        if (!numberOfDashboards)
            console.log(`No results found\n`.green);

    };

    console.log(colors.green.bold(`-------- Have been found ${numberOfObjects} results in ${numberOfDashboards} dashboards -------\n`));

    process.exit();
}
main();