// Wallet and address deletion state transitions, kept out of the views so the
// selection and broadcast rules are testable without a DOM.

const { notify } = require("./browserApi");

// Two records of the same address can be stored in different cases, so
// address equality is never a literal string comparison.
function sameAddress(a, b) {
    if (a === null || a === undefined || b === null || b === undefined) {
        return false;
    }
    return String(a).toLowerCase() === String(b).toLowerCase();
}

// Forget every site permission held against the given addresses.
function dropSitePermissions(state, addresses) {
    for (const addr of addresses) {
        delete state.allowedSites[addr];
        delete state.deniedSites[addr];
    }
}

// Remove wallet `walletIdx` from `state` and repair the derived state.
//
// Rules:
//   - `hasWallet` tracks whether any wallet remains.
//   - Site permissions are dropped for every address of the deleted wallet.
//   - `selectedWallet` follows the splice: it is decremented when a wallet
//     before it was removed, and falls back to the first remaining wallet's
//     first address only when the selection itself was deleted.
//   - `activeAddress` is only moved when it belonged to the deleted wallet;
//     the fallback is the first remaining wallet's first address, or null
//     when no wallet remains.
//
// Returns whether `activeAddress` changed, so the caller can broadcast it.
function removeWalletFromState(state, walletIdx) {
    const wallet = state.wallets[walletIdx];
    const addresses = (wallet.addresses || []).map((a) => a.address);
    const previousActive = state.activeAddress;
    const activeWasDeleted = addresses.some((a) =>
        sameAddress(a, previousActive),
    );

    state.wallets.splice(walletIdx, 1);

    dropSitePermissions(state, addresses);

    state.hasWallet = state.wallets.length > 0;

    const fallbackAddress = state.hasWallet
        ? state.wallets[0].addresses[0]?.address || null
        : null;

    if (!state.hasWallet) {
        state.selectedWallet = null;
        state.selectedAddress = null;
    } else if (state.selectedWallet === walletIdx) {
        state.selectedWallet = 0;
        state.selectedAddress = 0;
    } else if (
        typeof state.selectedWallet === "number" &&
        state.selectedWallet > walletIdx
    ) {
        state.selectedWallet -= 1;
    }

    if (activeWasDeleted || !state.hasWallet) {
        state.activeAddress = fallbackAddress;
    }

    return { activeAddressChanged: state.activeAddress !== previousActive };
}

// Whether a wallet may be offered a per-address remove control, and the same
// gate the removal itself is held behind.
//
// Only a wallet that derives its addresses from an extended key can hold more
// than one, so only those get the control — a key wallet has exactly one
// address and no "+" button either. The last address of any wallet is never
// removable: a wallet with no addresses is what delete-wallet is for.
function canRemoveAddress(wallet) {
    if (!wallet) return false;
    if (wallet.type !== "hd" && wallet.type !== "xprv") return false;
    return (wallet.addresses || []).length > 1;
}

// Remove address `addrIdx` of wallet `walletIdx` and repair the derived state.
//
// Nothing is destroyed here. The address stays derivable from the wallet's own
// key material and any funds at it are untouched; this only stops the wallet
// tracking it. `nextIndex` is deliberately left alone — it is a derivation
// high-water mark, so "+" derives a fresh index rather than handing back the
// address just removed, and the gap it leaves is within what
// `scanForAddresses()` re-discovers on a later import.
//
// The rules mirror removeWalletFromState() one level down:
//   - The call is refused unless canRemoveAddress() allows it, so the last
//     address of a wallet always survives.
//   - Site permissions are dropped for the removed address.
//   - `selectedAddress` follows the splice, but only within the wallet that
//     lost the address: it is decremented when an earlier address was
//     removed, and falls back to that wallet's first address when the
//     selection itself was removed. `selectedWallet` never moves, because the
//     wallet list does not.
//   - `activeAddress` moves only when it was the removed address, and then to
//     the wallet's first remaining address.
//
// Returns whether the address was removed and whether `activeAddress`
// changed, so the caller can broadcast it.
function removeAddressFromState(state, walletIdx, addrIdx) {
    const wallet = state.wallets[walletIdx];
    const refused = { removed: false, activeAddressChanged: false };
    if (!canRemoveAddress(wallet)) return refused;
    if (!wallet.addresses[addrIdx]) return refused;

    const address = wallet.addresses[addrIdx].address;
    const previousActive = state.activeAddress;
    const activeWasRemoved = sameAddress(address, previousActive);

    wallet.addresses.splice(addrIdx, 1);

    dropSitePermissions(state, [address]);

    if (state.selectedWallet === walletIdx) {
        if (state.selectedAddress === addrIdx) {
            state.selectedAddress = 0;
        } else if (
            typeof state.selectedAddress === "number" &&
            state.selectedAddress > addrIdx
        ) {
            state.selectedAddress -= 1;
        }
    }

    if (activeWasRemoved) {
        state.activeAddress = wallet.addresses[0].address;
    }

    return {
        removed: true,
        activeAddressChanged: state.activeAddress !== previousActive,
    };
}

// Tell the background the active address changed, so it re-emits
// accountsChanged to connected sites. Same call shape as the address
// switch in the home view.
function broadcastActiveChanged() {
    notify({ type: "AUTISTMASK_ACTIVE_CHANGED" });
}

module.exports = {
    canRemoveAddress,
    removeAddressFromState,
    removeWalletFromState,
    broadcastActiveChanged,
};
