const {
    $,
    showView,
    showFlash,
    goBack,
    clearViewStack,
    onViewLeave,
} = require("./helpers");
const {
    generateMnemonic,
    hdWalletFromMnemonic,
    isValidMnemonic,
    addressFromPrivateKey,
    hdWalletFromXprv,
    isValidXprv,
    isMasterExtendedKey,
} = require("../../shared/wallet");
const { encryptWithPassword } = require("../../shared/vault");
const { state, saveState } = require("../../shared/state");
const { scanForAddresses } = require("../../shared/balances");

/**
 * Check if an address already exists in ANY wallet (hd, xprv, or key).
 * Returns the wallet object if found, or undefined.
 */
function findWalletByAddress(addr) {
    const lower = addr.toLowerCase();
    return state.wallets.find((w) =>
        w.addresses.some((a) => a.address.toLowerCase() === lower),
    );
}

/**
 * Check if an xpub already exists in any HD-type wallet (hd or xprv).
 * Returns the wallet object if found, or undefined.
 */
function findWalletByXpub(xpub) {
    return state.wallets.find((w) => w.xpub && w.xpub === xpub);
}

let currentMode = "mnemonic";

const MODES = ["mnemonic", "privkey", "xprv"];

// Each hint names what this import mode's own backup is, because a key
// wallet and an xprv wallet have no recovery phrase to point the user at.
// All three say the same thing about the password: it is gone for good if
// it is forgotten. That sentence is the only warning the user gets before
// the wallet exists, and without it the lost-password route in
// views/deleteWallet.js is the first they hear of it.
//
// Keep the three within a couple of characters of each other in length.
// The hint sits directly above the password fields and the tabs swap it in
// place, so a wording that wraps to a different number of lines would move
// those fields under the pointer; the reserved height on
// #add-wallet-password-hint is the other half of that guarantee.
const PASSWORD_HINTS = {
    mnemonic:
        "This password encrypts your recovery phrase on this device. You will need it to send funds. It cannot be recovered or reset, so keep your recovery phrase written down: it is the only backup of this wallet.",
    privkey:
        "This password encrypts your private key on this device. You will need it to send funds. It cannot be recovered or reset, so keep your private key saved somewhere safe: it is the only backup of this wallet.",
    xprv: "This password encrypts your key on this device. You will need it to send funds. It cannot be recovered or reset, so keep your extended private key saved somewhere safe: it is the only backup of this wallet.",
};

function switchMode(mode) {
    currentMode = mode;
    for (const m of MODES) {
        $("add-wallet-section-" + m).classList.toggle("hidden", m !== mode);
        const tab = $("tab-" + m);
        const isActive = m === mode;
        // Active: bold, solid border on top/sides, no bottom border (connects to content)
        tab.classList.toggle("font-bold", isActive);
        tab.classList.toggle("border-solid", isActive);
        tab.classList.toggle("border-border", isActive);
        tab.classList.toggle("border-b-bg", isActive);
        tab.classList.toggle("bg-bg", isActive);
        // Inactive: muted text, dashed border on top/sides, transparent bottom, hover invert
        tab.classList.toggle("text-muted", !isActive);
        tab.classList.toggle("border-dashed", !isActive);
        tab.classList.toggle("border-border-light", !isActive);
        tab.classList.toggle("border-b-transparent", !isActive);
        tab.classList.toggle("hover:bg-fg", !isActive);
        tab.classList.toggle("hover:text-bg", !isActive);
    }
    $("add-wallet-password-hint").textContent = PASSWORD_HINTS[mode];
}

// Wipe the secret material this screen holds in the DOM: a generated or
// pasted recovery phrase, an imported private key or extended private key,
// and the password that would encrypt them. Registered as the view-leave
// handler as well as run on entry, so none of it survives in the hidden
// view after the user navigates away by any route, including the Settings
// gear and the import itself.
function clear() {
    $("wallet-mnemonic").value = "";
    $("import-private-key").value = "";
    $("import-xprv-key").value = "";
    $("add-wallet-password").value = "";
    $("add-wallet-password-confirm").value = "";
    $("add-wallet-phrase-warning").style.visibility = "hidden";
}

function show() {
    clear();
    switchMode("mnemonic");
    showView("add-wallet");
}

function validatePassword() {
    const pw = $("add-wallet-password").value;
    const pw2 = $("add-wallet-password-confirm").value;
    if (!pw) {
        showFlash("Please choose a password.");
        return null;
    }
    if (pw.length < 12) {
        showFlash("Password must be at least 12 characters.");
        return null;
    }
    if (pw !== pw2) {
        showFlash("Passwords do not match.");
        return null;
    }
    return pw;
}

async function importMnemonic(ctx) {
    const mnemonic = $("wallet-mnemonic").value.trim();
    if (!mnemonic) {
        showFlash("Enter a recovery phrase or press the die to generate one.");
        return;
    }
    const words = mnemonic.split(/\s+/);
    if (words.length !== 12 && words.length !== 24) {
        showFlash(
            "Recovery phrase must be 12 or 24 words. You entered " +
                words.length +
                ".",
        );
        return;
    }
    if (!isValidMnemonic(mnemonic)) {
        showFlash("Invalid recovery phrase. Check for typos.");
        return;
    }
    const pw = validatePassword();
    if (!pw) return;
    const { xpub, firstAddress } = hdWalletFromMnemonic(mnemonic);
    const xpubDup = findWalletByXpub(xpub);
    if (xpubDup) {
        showFlash(
            "This recovery phrase is already added (" + xpubDup.name + ").",
        );
        return;
    }
    const addrDup = findWalletByAddress(firstAddress);
    if (addrDup) {
        showFlash("Address already exists in wallet (" + addrDup.name + ").");
        return;
    }
    const encrypted = await encryptWithPassword(mnemonic, pw);
    const walletNum = state.wallets.length + 1;
    const wallet = {
        type: "hd",
        name: "Wallet " + walletNum,
        xpub: xpub,
        encryptedSecret: encrypted,
        nextIndex: 1,
        addresses: [
            { address: firstAddress, balance: "0.0000", tokenBalances: [] },
        ],
    };
    state.wallets.push(wallet);
    state.hasWallet = true;
    await saveState();
    clearViewStack();
    ctx.renderWalletList();
    showView("main");

    // Scan for used HD addresses beyond index 0.
    showFlash("Scanning for addresses...", 30000);
    const scan = await scanForAddresses(xpub, state.rpcUrl, state.networkId);
    if (scan.addresses.length > 1) {
        wallet.addresses = scan.addresses.map((a) => ({
            address: a.address,
            balance: "0.0000",
            tokenBalances: [],
        }));
        wallet.nextIndex = scan.nextIndex;
        await saveState();
        ctx.renderWalletList();
        showFlash("Found " + scan.addresses.length + " addresses.");
    } else {
        showFlash("Ready.", 1000);
    }

    ctx.doRefreshAndRender();
}

async function importPrivateKey(ctx) {
    const key = $("import-private-key").value.trim();
    if (!key) {
        showFlash("Please enter your private key.");
        return;
    }
    let addr;
    try {
        addr = addressFromPrivateKey(key);
    } catch {
        showFlash("Invalid private key.");
        return;
    }
    const pw = validatePassword();
    if (!pw) return;
    const duplicate = findWalletByAddress(addr);
    if (duplicate) {
        showFlash(
            "This address already exists in wallet (" + duplicate.name + ").",
        );
        return;
    }
    const encrypted = await encryptWithPassword(key, pw);
    const walletNum = state.wallets.length + 1;
    state.wallets.push({
        type: "key",
        name: "Wallet " + walletNum,
        encryptedSecret: encrypted,
        addresses: [{ address: addr, balance: "0.0000", tokenBalances: [] }],
    });
    state.hasWallet = true;
    await saveState();
    clearViewStack();
    ctx.renderWalletList();
    showView("main");

    ctx.doRefreshAndRender();
}

async function importXprvKey(ctx) {
    const xprv = $("import-xprv-key").value.trim();
    if (!xprv) {
        showFlash("Please enter your extended private key.");
        return;
    }
    if (!isValidXprv(xprv)) {
        showFlash(
            "That extended private key is not valid. Please check it and try again.",
        );
        return;
    }
    if (!isMasterExtendedKey(xprv)) {
        showFlash(
            "That is an account-level or child key, which cannot be imported. " +
                "Please paste the master extended private key for the wallet.",
        );
        return;
    }
    let result;
    try {
        result = hdWalletFromXprv(xprv);
    } catch {
        showFlash(
            "That extended private key is not valid. Please check it and try again.",
        );
        return;
    }
    const { xpub, firstAddress } = result;
    const xpubDup = findWalletByXpub(xpub);
    if (xpubDup) {
        showFlash("This key is already added (" + xpubDup.name + ").");
        return;
    }
    const addrDup = findWalletByAddress(firstAddress);
    if (addrDup) {
        showFlash("Address already exists in wallet (" + addrDup.name + ").");
        return;
    }
    const pw = validatePassword();
    if (!pw) return;
    const encrypted = await encryptWithPassword(xprv, pw);
    const walletNum = state.wallets.length + 1;
    const wallet = {
        type: "xprv",
        name: "Wallet " + walletNum,
        xpub: xpub,
        encryptedSecret: encrypted,
        nextIndex: 1,
        addresses: [
            { address: firstAddress, balance: "0.0000", tokenBalances: [] },
        ],
    };
    state.wallets.push(wallet);
    state.hasWallet = true;
    await saveState();
    clearViewStack();
    ctx.renderWalletList();
    showView("main");

    // Scan for used HD addresses beyond index 0.
    showFlash("Scanning for addresses...", 30000);
    const scan = await scanForAddresses(xpub, state.rpcUrl, state.networkId);
    if (scan.addresses.length > 1) {
        wallet.addresses = scan.addresses.map((a) => ({
            address: a.address,
            balance: "0.0000",
            tokenBalances: [],
        }));
        wallet.nextIndex = scan.nextIndex;
        await saveState();
        ctx.renderWalletList();
        showFlash("Found " + scan.addresses.length + " addresses.");
    } else {
        showFlash("Ready.", 1000);
    }

    ctx.doRefreshAndRender();
}

function init(ctx) {
    onViewLeave("add-wallet", clear);

    // Tab click handlers
    $("tab-mnemonic").addEventListener("click", () => switchMode("mnemonic"));
    $("tab-privkey").addEventListener("click", () => switchMode("privkey"));
    $("tab-xprv").addEventListener("click", () => switchMode("xprv"));

    // Generate mnemonic
    $("btn-generate-phrase").addEventListener("click", () => {
        $("wallet-mnemonic").value = generateMnemonic();
        $("add-wallet-phrase-warning").style.visibility = "visible";
    });

    // Import / confirm
    $("btn-add-wallet-confirm").addEventListener("click", async () => {
        if (currentMode === "mnemonic") {
            await importMnemonic(ctx);
        } else if (currentMode === "privkey") {
            await importPrivateKey(ctx);
        } else if (currentMode === "xprv") {
            await importXprvKey(ctx);
        }
    });

    // Back button
    $("btn-add-wallet-back").addEventListener("click", () => {
        goBack();
    });
}

module.exports = { init, show };
