import { BehaviorSubject } from "rxjs";
import { ProxySigner } from "./proxy-signer.js";
export class AccountManager {
    types = new Map();
    active$ = new BehaviorSubject(undefined);
    get active() {
        return this.active$.value;
    }
    accounts$ = new BehaviorSubject([]);
    get accounts() {
        return this.accounts$.value;
    }
    /** Proxy signer for currently active account */
    signer;
    /** Disable request queueing for any accounts added to this manager */
    disableQueue;
    constructor() {
        this.signer = new ProxySigner(this.active$, "No active account");
    }
    // Account type CRUD
    /** Add account type class */
    registerType(accountType) {
        if (!accountType.type)
            throw new Error(`Account class missing static "type" field`);
        if (this.types.has(accountType.type))
            throw new Error(`An account type of ${accountType.type} already exists`);
        this.types.set(accountType.type, accountType);
    }
    /** Remove account type */
    unregisterType(type) {
        this.types.delete(type);
    }
    // Accounts CRUD
    /** gets an account in the manager */
    getAccount(id) {
        if (typeof id === "string")
            return this.accounts$.value.find((a) => a.id === id);
        else if (this.accounts$.value.includes(id))
            return id;
        else
            return undefined;
    }
    /** Return the first account for a pubkey */
    getAccountForPubkey(pubkey) {
        return Object.values(this.accounts$.value).find((account) => account.pubkey === pubkey);
    }
    /** Returns all accounts for a pubkey */
    getAccountsForPubkey(pubkey) {
        return Object.values(this.accounts$.value).filter((account) => account.pubkey === pubkey);
    }
    /** adds an account to the manager */
    addAccount(account) {
        if (this.getAccount(account.id))
            return;
        // copy the disableQueue flag only if its set
        if (this.disableQueue !== undefined && account.disableQueue !== undefined) {
            account.disableQueue = this.disableQueue;
        }
        this.accounts$.next([...this.accounts$.value, account]);
    }
    /** Removes an account from the manager */
    removeAccount(account) {
        const id = typeof account === "string" ? account : account.id;
        this.accounts$.next(this.accounts$.value.filter((a) => a.id !== id));
        // if the removed account was active, clear the active account
        if (this.active$.value?.id === id)
            this.active$.next(undefined);
    }
    /** Replaces an account with another */
    replaceAccount(old, account) {
        this.addAccount(account);
        // if the old account was active, switch to the new one
        const id = typeof account === "string" ? account : account.id;
        if (this.active$.value?.id === id)
            this.setActive(account);
        this.removeAccount(old);
    }
    // Active account methods
    /** Returns the currently active account */
    getActive() {
        return this.active$.value;
    }
    /** Sets the currently active account */
    setActive(id) {
        const account = this.getAccount(id);
        if (!account)
            throw new Error("Cant find account with that ID");
        if (this.active$.value?.id !== account.id) {
            this.active$.next(account);
        }
    }
    /** Clears the currently active account */
    clearActive() {
        this.active$.next(undefined);
    }
    // Metadata CRUD
    /** sets the metadata on an account */
    setAccountMetadata(id, metadata) {
        const account = this.getAccount(id);
        if (!account)
            throw new Error("Cant find account with that ID");
        account.metadata = metadata;
    }
    /** sets the metadata on an account */
    getAccountMetadata(id) {
        const account = this.getAccount(id);
        if (!account)
            throw new Error("Cant find account with that ID");
        return account.metadata;
    }
    /** Removes all metadata on the account */
    clearAccountMetadata(id) {
        const account = this.getAccount(id);
        if (!account)
            throw new Error("Cant find account with that ID");
        account.metadata = undefined;
    }
    // Serialize / Deserialize
    /** Returns an array of serialized accounts */
    toJSON(quite = false) {
        const accounts = [];
        for (const account of this.accounts) {
            try {
                accounts.push(account.toJSON());
            }
            catch (error) {
                if (!quite)
                    throw error;
            }
        }
        return accounts;
    }
    /**
     * Restores all accounts from an array of serialized accounts
     * NOTE: this will clear all existing accounts
     */
    fromJSON(accounts, quite = false) {
        for (const json of accounts) {
            try {
                const AccountType = this.types.get(json.type);
                if (!AccountType) {
                    if (!quite)
                        throw new Error(`Missing account type ${json.type}`);
                    else
                        continue;
                }
                const account = AccountType.fromJSON(json);
                this.addAccount(account);
            }
            catch (error) {
                if (!quite)
                    throw error;
                else
                    console.log(`Failed to load account`, error);
            }
        }
    }
}
