const inquirer = require("inquirer");
const { Command } = require("commander");
const Client = require("../client");
const { sdkForConsole } = require("../sdks");
const { globalConfig, localConfig } = require("../config");
const { actionRunner, success, parseBool, commandDescriptions, error, parse, hint, log, drawTable, cliConfig } = require("../parser");
const ID = require("../id");
{% if sdk.test != "true" %}
const { questionsLogin, questionsLogout, questionsListFactors, questionsMfaChallenge } = require("../questions");
const { accountUpdateMfaChallenge, accountCreateMfaChallenge, accountGet, accountCreateEmailPasswordSession, accountDeleteSession } = require("./account");

const DEFAULT_ENDPOINT = 'https://cloud.appwrite.io/v1';

const loginCommand = async ({ email, password, endpoint, mfa, code }) => {
    const oldCurrent = globalConfig.getCurrentSession();

    const configEndpoint = (endpoint ?? globalConfig.getEndpoint()) || DEFAULT_ENDPOINT;

    if (globalConfig.getCurrentSession() !== '') {
        log('You are currently signed in as ' + globalConfig.getEmail());

        if (globalConfig.getSessions().length === 1) {
            hint('You can sign in and manage multiple accounts with Appwrite CLI');
        }
    }

    const answers = email && password ? { email, password } : await inquirer.prompt(questionsLogin);

    if (!answers.method) {
        answers.method = 'login';
    }

    if (answers.method === 'select') {
        const accountId = answers.accountId;

        if (!globalConfig.getSessionIds().includes(accountId)) {
            throw Error('Session ID not found');
        }

        globalConfig.setCurrentSession(accountId);
        success(`Current account is ${accountId}`);

        return;
    }

    const id = ID.unique();

    globalConfig.addSession(id, {});
    globalConfig.setCurrentSession(id);
    globalConfig.setEndpoint(configEndpoint);
    globalConfig.setEmail(answers.email);

    let client = await sdkForConsole(false);

    let account;

    try {
        await accountCreateEmailPasswordSession({
            email: answers.email,
            password: answers.password,
            parseOutput: false,
            sdk: client
        })

        client.setCookie(globalConfig.getCookie());

        account = await accountGet({
            sdk: client,
            parseOutput: false
        });
    } catch (error) {
        if (error.response === 'user_more_factors_required') {
            const { factor } = mfa ? { factor: mfa } : await inquirer.prompt(questionsListFactors);

            const challenge = await accountCreateMfaChallenge({
                factor,
                parseOutput: false,
                sdk: client
            });

            const { otp } = code ? { otp: code } : await inquirer.prompt(questionsMfaChallenge);

            await accountUpdateMfaChallenge({
                challengeId: challenge.$id,
                otp,
                parseOutput: false,
                sdk: client
            });

            account = await accountGet({
                sdk: client,
                parseOutput: false
            });
        } else {
            globalConfig.removeSession(id);
            globalConfig.setCurrentSession(oldCurrent);
            if (endpoint !== DEFAULT_ENDPOINT && error.response === 'user_invalid_credentials') {
                log('Use the --endpoint option for self-hosted instances')
            }
            throw error;
        }
    }

    success("Successfully signed in as " + account.email);
    hint("Next you can create or link to your project using 'appwrite init project'");
};

const whoami = new Command("whoami")
    .description(commandDescriptions['whoami'])
    .action(actionRunner(async () => {
        if (globalConfig.getEndpoint() === '' || globalConfig.getCookie() === '') {
            error("No user is signed in. To sign in, run 'appwrite login'");
            return;
        }

        let client = await sdkForConsole(false);

        let account;

        try {
            account = await accountGet({
                sdk: client,
                parseOutput: false
            });
        } catch (error) {
            error("No user is signed in. To sign in, run 'appwrite login'");
            return;
        }

        const data = [
            {
                'ID': account.$id,
                'Name': account.name,
                'Email': account.email,
                'MFA enabled': account.mfa ? 'Yes' : 'No',
                'Endpoint': globalConfig.getEndpoint()
            }
        ];

        if (cliConfig.json) {
            console.log(data);
            return;
        }

        drawTable(data)
    }));

const register = new Command("register")
    .description(commandDescriptions['register'])
    .action(actionRunner(async () => {
        log('Visit https://cloud.appwrite.io/register to create an account')
    }));

const login = new Command("login")
    .description(commandDescriptions['login'])
    .option(`--email [email]`, `User email`)
    .option(`--password [password]`, `User password`)
    .option(`--endpoint [endpoint]`, `Appwrite endpoint for self hosted instances`)
    .option(`--mfa [factor]`, `Multi-factor authentication login factor: totp, email, phone or recoveryCode`)
    .option(`--code [code]`, `Multi-factor code`)
    .configureHelp({
        helpWidth: process.stdout.columns || 80
    })
    .action(actionRunner(loginCommand));

const deleteSession = async (accountId) => {
    try {
        let client = await sdkForConsole();

        await accountDeleteSession({
            sessionId: 'current',
            parseOutput: false,
            sdk: client
        })

        globalConfig.removeSession(accountId);
    } catch (e) {
        error('Unable to log out, removing locally saved session information')
    }
    globalConfig.removeSession(accountId);
}

const logout = new Command("logout")
    .description(commandDescriptions['logout'])
    .configureHelp({
        helpWidth: process.stdout.columns || 80
    })
    .action(actionRunner(async () => {
        const sessions = globalConfig.getSessions();
        const current = globalConfig.getCurrentSession();

        if (current === '' || !sessions.length) {
            log('No active sessions found.');
            return;
        }
        if (sessions.length === 1) {
            await deleteSession(current);
            success("Logging out");

            return;
        }

        const answers = await inquirer.prompt(questionsLogout);

        if (answers.accounts) {
            for (let accountId of answers.accounts) {
                globalConfig.setCurrentSession(accountId);
                await deleteSession(accountId);
            }
        }

        const remainingSessions = globalConfig.getSessions();

        if (remainingSessions.length > 0 && remainingSessions.filter(session => session.id === current).length !== 1) {
            const accountId = remainingSessions [0].id;
            globalConfig.setCurrentSession(accountId);

            success(`Current account is ${accountId}`);
        }

        success("Logging out");
    }));
{% endif %}

const client = new Command("client")
    .description(commandDescriptions['client'])
    .configureHelp({
        helpWidth: process.stdout.columns || 80
    })
    .option("-ss, --self-signed <value>", "Configure the CLI to use a self-signed certificate ( true or false )", parseBool)
    .option("-e, --endpoint <endpoint>", "Set your {{ spec.title|caseUcfirst }} server endpoint")
    .option("-p, --project-id <project-id>", "Set your {{ spec.title|caseUcfirst }} project ID")
    .option("-k, --key <key>", "Set your {{ spec.title|caseUcfirst }} server's API key")
    .option("-d, --debug", "Print CLI debug information")
    .option("-r, --reset", "Reset the CLI configuration")
    .action(actionRunner(async ({ selfSigned, endpoint, projectId, key, debug, reset }, command) => {
        if (selfSigned == undefined && endpoint == undefined && projectId == undefined && key == undefined && debug == undefined && reset == undefined) {
            command.help()
        }

        if (debug) {
            let config = {
                endpoint: globalConfig.getEndpoint(),
                key: globalConfig.getKey(),
                cookie: globalConfig.getCookie(),
                selfSigned: globalConfig.getSelfSigned(),
                project: localConfig.getProject()
            }
            parse(config)
        }

        if (endpoint !== undefined) {
            try {
                const id = ID.unique();
                let url = new URL(endpoint);
                if (url.protocol !== "http:" && url.protocol !== "https:") {
                    throw new Error();
                }

                let client = new Client().setEndpoint(endpoint);
                client.setProject('console');
                if (selfSigned || globalConfig.getSelfSigned()) {
                    client.setSelfSigned(true);
                }
                let response = await client.call('GET', '/health/version');
                if (!response.version) {
                    throw new Error();
                }
                globalConfig.setCurrentSession(id);
                globalConfig.addSession(id, {});
                globalConfig.setEndpoint(endpoint);
            } catch (_) {
                throw new Error("Invalid endpoint or your Appwrite server is not running as expected.");
            }
        }

        if (key !== undefined) {
            globalConfig.setKey(key)
        }

        if (projectId !== undefined) {
            localConfig.setProject(projectId, '');
        }

        if (selfSigned == true || selfSigned == false) {
            globalConfig.setSelfSigned(selfSigned);
        }

        if (reset !== undefined) {
            const sessions = globalConfig.getSessions();

            for (let accountId of sessions.map(session => session.id)) {
                globalConfig.setCurrentSession(accountId);
                await deleteSession(accountId);
            }
        }

        success("Setting client")
    }));

const migrate = async () => {
    if (!globalConfig.has('endpoint') || !globalConfig.has('cookie')) {
        return;
    }

    const endpoint = globalConfig.get('endpoint');
    const cookie = globalConfig.get('cookie');

    const id = ID.unique();
    const data = {
        endpoint,
        cookie,
        email: 'legacy'
    };

    globalConfig.addSession(id, data);
    globalConfig.setCurrentSession(id);
    globalConfig.delete('endpoint');
    globalConfig.delete('cookie');

}
module.exports = {
    {% if sdk.test != "true" %}
    loginCommand,
    whoami,
    register,
    login,
    logout,
    {% endif %}
    migrate,
    client
};
