// Transaction detail view — shows full details for a single transaction.
// Shared by addressDetail and addressToken via ctx.showTransactionDetail().

const {
    $,
    showView,
    showFlash,
    flashCopyFeedback,
    addressTitle,
    addressDotHtml,
    escapeHtml,
    isoDate,
    timeAgo,
    renderAddressHtml,
    attachCopyHandlers,
    copyableHtml,
    etherscanLinkHtml,
    explorerUrl,
    displaySymbol,
    goBack,
} = require("./helpers");
const { state } = require("../../shared/state");
const { formatEther, formatUnits } = require("ethers");
const makeBlockie = require("ethereum-blockies-base64");
const { log, debugFetch } = require("../../shared/log");
const { decodeCalldata } = require("./approval");

/**
 * Determine a human-readable transaction type string from tx fields.
 */
function getTransactionType(tx) {
    if (!tx.to) return "Contract Creation";
    if (tx.direction === "contract") {
        if (tx.directionLabel === "Swap") return "Swap";
        if (
            tx.method === "approve" ||
            tx.directionLabel === "Approve" ||
            tx.method === "setApprovalForAll"
        )
            return "Token Approval";
        return "Contract Call";
    }
    if (tx.symbol && tx.symbol !== "ETH") return "ERC-20 Token Transfer";
    return "Native ETH Transfer";
}

function blockieHtml(address) {
    const src = makeBlockie(address);
    return `<img src="${escapeHtml(src)}" width="48" height="48" style="image-rendering:pixelated;border-radius:50%;display:inline-block">`;
}

function txAddressHtml(address, ensName, title) {
    const blockie = blockieHtml(address);
    return (
        `<div class="mb-1">${blockie}</div>` +
        renderAddressHtml(address, { title, ensName })
    );
}

function txHashHtml(hash) {
    const link = explorerUrl("tx", hash);
    const extLink = etherscanLinkHtml(link);
    return copyableHtml(hash, "break-all") + extLink;
}

function show(tx) {
    state.viewData = {
        tx: {
            hash: tx.hash,
            from: tx.from,
            to: tx.to,
            value: tx.value,
            exactValue: tx.exactValue || tx.value,
            rawAmount: tx.rawAmount || "",
            rawUnit: tx.rawUnit || "",
            symbol: tx.symbol,
            timestamp: tx.timestamp,
            isError: tx.isError,
            fromEns: tx.fromEns || null,
            toEns: tx.toEns || null,
            directionLabel: tx.directionLabel || null,
            direction: tx.direction || null,
            isContractCall: tx.isContractCall || false,
            method: tx.method || null,
            contractAddress: tx.contractAddress || null,
        },
    };
    render();
}

function render() {
    const tx = state.viewData.tx;
    if (!tx) return;
    $("tx-detail-hash").innerHTML = txHashHtml(tx.hash);

    const fromTitle = addressTitle(tx.from, state.wallets);
    const toTitle = addressTitle(tx.to, state.wallets);
    $("tx-detail-from").innerHTML = txAddressHtml(
        tx.from,
        tx.fromEns,
        fromTitle,
    );
    $("tx-detail-to").innerHTML = txAddressHtml(tx.to, tx.toEns, toTitle);

    // Exact amount (full precision, copyable)
    const detailSym = displaySymbol(tx.symbol);
    const exactStr = tx.exactValue
        ? tx.exactValue + " " + detailSym
        : tx.directionLabel + " " + detailSym;
    $("tx-detail-value").innerHTML = copyableHtml(exactStr, "font-bold");

    // Native quantity (raw integer, copyable)
    const nativeEl = $("tx-detail-native");
    if (tx.rawAmount && tx.rawUnit) {
        const nativeStr = tx.rawAmount + " " + tx.rawUnit;
        nativeEl.innerHTML = copyableHtml(nativeStr, "");
        nativeEl.parentElement.classList.remove("hidden");
    } else {
        nativeEl.innerHTML = "";
        nativeEl.parentElement.classList.add("hidden");
    }

    // Always show transaction type as the first field
    const typeSection = $("tx-detail-type-section");
    const typeEl = $("tx-detail-type");
    const headingEl = $("tx-detail-heading");
    if (typeSection && typeEl) {
        typeEl.textContent = getTransactionType(tx);
        typeSection.classList.remove("hidden");
    }
    if (headingEl) headingEl.textContent = "Transaction";

    // Token contract address (for ERC-20 transfers)
    const tokenContractSection = $("tx-detail-token-contract-section");
    const tokenContractEl = $("tx-detail-token-contract");
    if (tokenContractSection && tokenContractEl) {
        if (tx.contractAddress) {
            const dot = addressDotHtml(tx.contractAddress);
            const link = explorerUrl("token", tx.contractAddress);
            tokenContractEl.innerHTML =
                `<div class="flex items-center">${dot}` +
                copyableHtml(tx.contractAddress, "break-all") +
                etherscanLinkHtml(link) +
                `</div>`;
            tokenContractSection.classList.remove("hidden");
        } else {
            tokenContractSection.classList.add("hidden");
        }
    }

    // Hide calldata and raw data sections; always fetch full tx details
    const calldataSection = $("tx-detail-calldata-section");
    if (calldataSection) calldataSection.classList.add("hidden");
    const rawDataSection = $("tx-detail-rawdata-section");
    if (rawDataSection) rawDataSection.classList.add("hidden");

    // Hide on-chain detail sections until populated
    for (const id of [
        "tx-detail-block-section",
        "tx-detail-nonce-section",
        "tx-detail-fee-section",
        "tx-detail-gasprice-section",
        "tx-detail-gasused-section",
        "tx-detail-network-section",
    ]) {
        const el = $(id);
        if (el) el.classList.add("hidden");
    }

    loadFullTxDetails(tx.hash, tx.to);

    const isoStr = isoDate(tx.timestamp);
    $("tx-detail-time").innerHTML =
        copyableHtml(isoStr) + " (" + escapeHtml(timeAgo(tx.timestamp)) + ")";
    $("tx-detail-status").textContent = tx.isError ? "Failed" : "Success";
    showView("transaction");
    attachCopyHandlers("view-transaction");
}

function showDetailField(sectionId, contentId, value) {
    const section = $(sectionId);
    const el = $(contentId);
    if (!section || !el) return;
    el.innerHTML = copyableHtml(value, "");
    section.classList.remove("hidden");
}

function populateOnChainDetails(txData) {
    // Block number
    if (txData.block_number != null) {
        const blockLink = explorerUrl("block", String(txData.block_number));
        const blockSection = $("tx-detail-block-section");
        const blockEl = $("tx-detail-block");
        if (blockSection && blockEl) {
            blockEl.innerHTML =
                copyableHtml(String(txData.block_number), "") +
                etherscanLinkHtml(blockLink);
            blockSection.classList.remove("hidden");
        }
    }

    // Nonce
    if (txData.nonce != null) {
        showDetailField(
            "tx-detail-nonce-section",
            "tx-detail-nonce",
            String(txData.nonce),
        );
    }

    // Transaction fee
    const feeWei = txData.fee?.value || txData.tx_fee;
    if (feeWei) {
        const feeEth = formatEther(String(feeWei));
        showDetailField(
            "tx-detail-fee-section",
            "tx-detail-fee",
            feeEth + " ETH",
        );
    }

    // Gas price
    const gasPrice = txData.gas_price;
    if (gasPrice) {
        const gwei = formatUnits(String(gasPrice), "gwei");
        showDetailField(
            "tx-detail-gasprice-section",
            "tx-detail-gasprice",
            gwei + " Gwei",
        );
    }

    // Gas used
    const gasUsed = txData.gas_used;
    if (gasUsed) {
        showDetailField(
            "tx-detail-gasused-section",
            "tx-detail-gasused",
            String(gasUsed),
        );
    }

    // Show the network details wrapper if any child section is visible
    const networkWrapper = $("tx-detail-network-section");
    if (networkWrapper) {
        const hasVisible = [
            "tx-detail-nonce-section",
            "tx-detail-fee-section",
            "tx-detail-gasprice-section",
            "tx-detail-gasused-section",
        ].some((id) => {
            const el = $(id);
            return el && !el.classList.contains("hidden");
        });
        if (hasVisible) networkWrapper.classList.remove("hidden");
    }

    // Bind copy handlers for newly added elements
    for (const id of [
        "tx-detail-block-section",
        "tx-detail-nonce-section",
        "tx-detail-fee-section",
        "tx-detail-gasprice-section",
        "tx-detail-gasused-section",
    ]) {
        const section = $(id);
        if (!section) continue;
        section.querySelectorAll("[data-copy]").forEach((el) => {
            el.onclick = () => {
                navigator.clipboard.writeText(el.dataset.copy);
                showFlash("Copied!");
                flashCopyFeedback(el);
            };
        });
    }
}

async function loadFullTxDetails(txHash, toAddress) {
    const section = $("tx-detail-calldata-section");
    const actionEl = $("tx-detail-calldata-action");
    const detailsEl = $("tx-detail-calldata-details");
    const wellEl = $("tx-detail-calldata-well");
    const rawSection = $("tx-detail-rawdata-section");
    const rawEl = $("tx-detail-rawdata");
    if (!section || !actionEl || !detailsEl) return;

    try {
        const resp = await debugFetch(
            state.blockscoutUrl + "/transactions/" + txHash,
        );
        if (!resp.ok) return;
        const txData = await resp.json();

        // Populate on-chain detail fields (block, nonce, gas, fee)
        populateOnChainDetails(txData);

        const inputData = txData.raw_input || txData.input || null;
        if (!inputData || inputData === "0x") return;

        const decoded = decodeCalldata(inputData, toAddress || "");
        if (decoded) {
            // Render decoded calldata matching approval view style
            actionEl.textContent = decoded.name;
            let detailsHtml = "";
            if (decoded.description) {
                detailsHtml += `<div class="mb-2">${escapeHtml(decoded.description)}</div>`;
            }
            for (const d of decoded.details || []) {
                detailsHtml += `<div class="mb-2">`;
                detailsHtml += `<div class="text-muted">${escapeHtml(d.label)}</div>`;
                if (d.address && d.isToken) {
                    // Token entry: show symbol on its own line, then address via shared renderer
                    const tokenSymbol = d.value.match(/^(\S+)\s*\(/)?.[1];
                    if (tokenSymbol) {
                        detailsHtml += `<div class="font-bold">${escapeHtml(displaySymbol(tokenSymbol))}</div>`;
                    }
                    detailsHtml += renderAddressHtml(d.address);
                } else if (d.address) {
                    detailsHtml += renderAddressHtml(d.address);
                } else {
                    detailsHtml += `<div class="font-bold">${escapeHtml(d.value)}</div>`;
                }
                detailsHtml += `</div>`;
            }
            detailsEl.innerHTML = detailsHtml;
            if (wellEl) wellEl.classList.remove("hidden");
        } else {
            // Unknown contract call — show method name in well
            const method = txData.method || "Unknown contract call";
            actionEl.textContent = method;
            detailsEl.innerHTML = "";
            if (wellEl) wellEl.classList.remove("hidden");
        }

        // Always show raw data
        if (rawSection && rawEl) {
            rawEl.innerHTML = copyableHtml(inputData, "break-all");
            rawSection.classList.remove("hidden");
        }

        section.classList.remove("hidden");

        // Bind copy handlers for new elements (including raw data now outside section)
        const copyTargets = [section, rawSection].filter(Boolean);
        for (const container of copyTargets) {
            attachCopyHandlers(container);
        }
    } catch (e) {
        log.errorf("loadCalldata failed:", e.message);
    }
}

// The ctx this view is initialized with is unused: this module is the leaf of
// the navigation, and the other views reach it through their own ctx.
function init(_ctx) {
    $("btn-tx-back").addEventListener("click", () => {
        goBack();
    });
}

module.exports = { init, show, render };
