/*
 * Contains all of the logic for interacting with JMAP
 * None of the dependencies should leak, in types or otherwise
 */
import * as base64 from "base-64";
import * as jmapclient from "jmap-client-ts";
import { FetchTransport } from "jmap-client-ts/lib/utils/fetch-transport";

import { IAccount, ISession } from "./types";

type Callback = () => void;

export interface IAuth {
	email: string;
	password: string;
}

export interface ClientState {
	session: ISession | null;
}

export default class Client {
	// Get the currently active account
	account(accountId: string): IAccount | null {
		if (!(this.state.session && this.state.session.accounts)) return null;
		return this.state.session.accounts[accountId];
	}
	// All objects which currently are listening for changes
	callbacks: Array<Callback> = [];
	jclient: jmapclient.Client | null = null;
	state: ClientState = {
		session: null,
	};

	onChange(f: Callback) {
		this.callbacks.push(f);
	}

	// Ensure that login happens. If this is called many times, only login once.
	ensureLogin(auth: IAuth) {
		if (this.jclient != null) return;
		this.doLogin(auth);
	}
	// Make the request to get system metadata
	doLogin(auth: IAuth) {
		const domain = auth.email.split("@")[1];
		const well_known_url = "https://" + domain + "/.well-known/jmap";
		const basic_auth =
			"Basic " + base64.encode(auth.email + ":" + auth.password);

		this.jclient = new jmapclient.Client({
			accessToken: "fake token",
			httpHeaders: { Authorization: basic_auth },
			sessionUrl: well_known_url,
			transport: new FetchTransport(fetch.bind(window)),
		});

		this.jclient
			.fetchSession()
			.then(() => {
				this._onSession();
			})
			.catch((error) => console.error(error));

		return;
	}

	mailboxList(accountId: string, ids: Array<string>) {
		if (this.jclient == null) return;
		this.jclient
			.mailbox_get({
				accountId: accountId,
				ids: null,
			})
			.then((response) => {
				if (this.state.session == null) return;
				const account = this.state.session.accounts[response.accountId];
				account.mailboxes = response.list;
				this._triggerChange();
			});
	}

	_triggerChange() {
		this.callbacks.forEach((c) => {
			c();
		});
	}
	_onSession() {
		console.log("Session received");

		// For the type checker
		if (!this.jclient) return;

		const session = this.jclient.getSession();
		this.state.session = {
			...session,
			accounts: Object.fromEntries(
				Object.entries(session.accounts).map(([key, account]) => [
					key,
					{ ...account, id: key.toString(), mailboxes: [] },
				]),
			),
		};
		if (!this.state.session) return;
		this._triggerChange();
	}
}
