"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const MatrixClient_1 = require("./MatrixClient");
/**
 * Functions for interacting with Matrix prior to having an access token. Intended
 * to be used for logging in/registering to get a MatrixClient instance.
 *
 * By design, this limits the options used to create the MatrixClient. To specify
 * custom elements to the client, get the access token from the returned client
 * and create a new MatrixClient instance. Due to the nature of Matrix, it is
 * also recommended to use the homeserverUrl from the generated MatrixClient as
 * it may be different from that given to the MatrixAuth class.
 */
class MatrixAuth {
    /**
     * Creates a new MatrixAuth class for creating a MatrixClient
     * @param {string} homeserverUrl The homeserver URL to authenticate against.
     */
    constructor(homeserverUrl) {
        this.homeserverUrl = homeserverUrl;
        // nothing to do
    }
    /**
     * Generate a client with no access token so we can reuse the doRequest
     * logic already written.
     */
    createTemplateClient() {
        return new MatrixClient_1.MatrixClient(this.homeserverUrl, "");
    }
    /**
     * Performs simple registration using a password for the account. This will
     * assume the server supports the m.login.password flow for registration, and
     * will attempt to complete only that stage. The caller is expected to determine
     * if the homeserver supports registration prior to invocation.
     * @param {string} localpart The localpart (username) to register
     * @param {string} password The password to register with
     * @returns {Promise<MatrixClient>} Resolves to a logged-in MatrixClient
     */
    passwordRegister(localpart, password) {
        return __awaiter(this, void 0, void 0, function* () {
            // First try and complete the stage without UIA in hopes the server is kind to us:
            const body = {
                username: localpart,
                password: password,
            };
            let response;
            try {
                response = yield this.createTemplateClient().doRequest("POST", "/_matrix/client/r0/register", null, body);
            }
            catch (e) {
                if (e.statusCode === 401) {
                    if (typeof (e.body) === "string")
                        e.body = JSON.parse(e.body);
                    if (!e.body)
                        throw new Error(JSON.stringify(Object.keys(e)));
                    // 401 means we need to do UIA, so try and complete a stage
                    const sessionId = e.body['session'];
                    const expectedFlow = ["m.login.dummy"];
                    let hasFlow = false;
                    for (const flow of e.body['flows']) {
                        const stages = flow['stages'];
                        if (stages.length !== expectedFlow.length)
                            continue;
                        let stagesMatch = true;
                        for (let i = 0; i < stages.length; i++) {
                            if (stages[i] !== expectedFlow[i]) {
                                stagesMatch = false;
                                break;
                            }
                        }
                        if (stagesMatch) {
                            hasFlow = true;
                            break;
                        }
                    }
                    if (!hasFlow)
                        throw new Error("Failed to find appropriate login flow in User-Interactive Authentication");
                    body['auth'] = {
                        type: expectedFlow[0],
                        session: sessionId,
                    };
                    response = yield this.createTemplateClient().doRequest("POST", "/_matrix/client/r0/register", null, body);
                }
            }
            if (!response)
                throw new Error("Failed to register");
            const accessToken = response['access_token'];
            if (!accessToken)
                throw new Error("No access token returned");
            return new MatrixClient_1.MatrixClient(this.homeserverUrl, accessToken);
        });
    }
    /**
     * Performs simple password login with the homeserver. The caller is
     * expected to confirm if the homeserver supports this login flow prior
     * to invocation.
     * @param {string} username The username (localpart or user ID) to log in with
     * @param {string} password The password for the account
     * @returns {Promise<MatrixClient>} Resolves to a logged-in MatrixClient
     */
    passwordLogin(username, password) {
        return __awaiter(this, void 0, void 0, function* () {
            const body = {
                type: "m.login.password",
                identifier: {
                    type: "m.id.user",
                    user: username,
                },
                password: password,
            };
            const response = yield this.createTemplateClient().doRequest("POST", "/_matrix/client/r0/login", null, body);
            const accessToken = response["access_token"];
            if (!accessToken)
                throw new Error("Expected access token in response - got nothing");
            let homeserverUrl = this.homeserverUrl;
            if (response['well_known'] && response['well_known']['m.homeserver'] && response['well_known']['m.homeserver']['base_url']) {
                homeserverUrl = response['well_known']['m.homeserver']['base_url'];
            }
            return new MatrixClient_1.MatrixClient(homeserverUrl, accessToken);
        });
    }
}
exports.MatrixAuth = MatrixAuth;
