(function () {
    var EXCHANGE_RATE = 80000;
    var WITHDRAW_MIN_AMOUNT = 50;
    var params = new URLSearchParams(window.location.search);
    var isMock = params.get('mock') === '1';
    var currentMode = normalizeMode(params.get('mode')) || 'transfer';
    var identity = String(params.get('identity') || '').trim();
    var salaryAssetType = String(
        params.get('salaryType') || params.get('asset_type') || ''
    ).trim();
    var state = {
        balance: 0,
        paymentInfo: null,
        selectedPayee: null,
        isSearching: false,
        isSaving: false,
        isEditingAddress: false,
        isSavingContact: false,
        isSubmitting: false,
        contactInfo: '',
        historyItems: [],
        transferRateTiers: [],
    };
    var lastSearchTriggerAt = 0;
    var autoSearchTimer = 0;

    var mockBalances = {
        HOST_SALARY_USD: 30650.75,
        AGENCY_SALARY_USD: 30650.75,
        BD_SALARY_USD: 48620.8,
        ADMIN_SALARY_USD: 168250.5,
    };

    var mock = {
        balance: mockBalances[salaryAssetType] || 30650.75,
        paymentInfo: null,
        payees: {
            163003: {
                id: 'payee_163003',
                name: 'Yumi Star Agency',
                account: '163003',
                avatar: '',
            },
            163000: {
                id: 'payee_163000',
                name: 'quick_1780052629537',
                account: '163000',
                avatar: '',
            },
        },
        history: [
            {
                key: historyTitleKey(),
                title: historyTitle(),
                amount: 18400.25,
                balance: mockBalances[salaryAssetType] || 30650.75,
                type: 0,
                date: '2026-06-01 12:10',
            },
            {
                key: 'withdraw_exchange.history_exchange_to_golds',
                title: 'Exchange to golds',
                amount: 800,
                balance: 12250.5,
                type: 1,
                date: '2026-05-30 20:42',
            },
            {
                key: 'withdraw_exchange.history_gift_reward',
                title: 'Gift reward',
                amount: 8250.5,
                balance: 13050.5,
                type: 0,
                date: '2026-05-28 18:30',
            },
        ],
    };

    var inputs = {
        withdraw: $('withdrawAmount'),
        exchange: $('exchangeAmount'),
        transfer: $('transferAmount'),
    };

    function $(id) {
        return document.getElementById(id);
    }

    function t(key, fallback) {
        return window.HyAppI18n && window.HyAppI18n.t
            ? window.HyAppI18n.t(key, fallback)
            : fallback || key;
    }

    function toast(message) {
        if (window.HyAppToast) {
            window.HyAppToast.show(message);
            return;
        }
        window.alert(message);
    }

    function normalizeMode(mode) {
        var value = String(mode || '').trim();
        return ['withdraw', 'exchange', 'transfer'].indexOf(value) >= 0
            ? value
            : '';
    }

    function normalizeIdentity(value) {
        value = String(value || '')
            .trim()
            .toUpperCase()
            .replace(/-/g, '_');
        if (value === 'ADMIN') return 'BD_LEADER';
        if (['HOST', 'AGENCY', 'BD', 'BD_LEADER'].indexOf(value) >= 0)
            return value;
        if (salaryAssetType === 'AGENCY_SALARY_USD') return 'AGENCY';
        if (salaryAssetType === 'BD_SALARY_USD') return 'BD';
        if (salaryAssetType === 'ADMIN_SALARY_USD') return 'BD_LEADER';
        return 'HOST';
    }

    function api() {
        if (window.HyAppAPI && window.HyAppAPI.salaryWallet)
            return window.HyAppAPI.salaryWallet;
        return window.HyApp && window.HyApp.api
            ? window.HyApp.api.salaryWallet
            : null;
    }

    function userApi() {
        if (window.HyAppAPI && window.HyAppAPI.user)
            return window.HyAppAPI.user;
        return window.HyApp && window.HyApp.api ? window.HyApp.api.user : null;
    }

    function commandId(prefix) {
        return (
            prefix +
            '-' +
            Date.now() +
            '-' +
            Math.random().toString(16).slice(2)
        );
    }

    function money(value) {
        var number = Number(value || 0);
        return number.toLocaleString('en-US', {
            minimumFractionDigits: 2,
            maximumFractionDigits: 2,
        });
    }

    function moneyInput(value) {
        var number = Number(value || 0);
        return Number.isFinite(number) ? number.toFixed(2) : '0.00';
    }

    function amount() {
        var input = inputs[currentMode];
        return Number(input && input.value ? input.value : 0) || 0;
    }

    function amountText(value) {
        return moneyInput(value);
    }

    function usdMinorToNumber(value) {
        return Number(value || 0) / 100;
    }

    function salaryBalanceAfterToNumber(value) {
        if (value && typeof value === 'object') {
            return usdMinorToNumber(value.available_amount);
        }
        return usdMinorToNumber(value);
    }

    function findTransferRate(value) {
        var minor = Math.round(Number(value || 0) * 100);
        return (state.transferRateTiers || []).find(function (tier) {
            return (
                String(tier.status || 'active') === 'active' &&
                minor >= Number(tier.min_usd_minor || 0) &&
                minor <= Number(tier.max_usd_minor || 0)
            );
        });
    }

    function delay(value) {
        return new Promise(function (resolve) {
            window.setTimeout(function () {
                resolve(value);
            }, 260);
        });
    }

    function mockRequest(action, payload) {
        if (action === 'balance') return delay(mock.balance);
        if (action === 'payment') return delay(mock.paymentInfo);
        if (action === 'history') return delay(mock.history.slice());
        if (action === 'saveAddress') {
            mock.paymentInfo = {
                id: 'usdt_mock_1',
                cardNo: payload.cardNo,
            };
            return delay(mock.paymentInfo);
        }
        if (action === 'searchPayee') {
            var account = String(payload.account || '').trim();
            var payee = mock.payees[account];
            if (!payee && account && account !== '000000') {
                payee = {
                    id: 'payee_' + account,
                    name: 'User ' + account,
                    nameKey: 'withdraw_exchange.mock_user_name',
                    account: account,
                    avatar: '',
                };
            }
            return delay(payee || null);
        }
        if (action === 'submit') {
            mock.balance = Math.max(0, mock.balance - payload.amount);
            mock.history.unshift({
                title: titleForMode(payload.mode),
                amount: payload.amount,
                balance: mock.balance,
                type: 1,
                date: new Date().toISOString().replace('T', ' ').slice(0, 16),
            });
            return delay({ ok: true });
        }
        return delay(null);
    }

    function titleForMode(mode) {
        if (mode === 'withdraw')
            return t('withdraw_exchange.withdraw', 'Withdraw');
        if (mode === 'exchange')
            return t('withdraw_exchange.exchange', 'Exchange');
        return t('withdraw_exchange.transfer', 'Transfer');
    }

    function historyTitleKey() {
        if (salaryAssetType === 'BD_SALARY_USD')
            return 'withdraw_exchange.history_bd_salary';
        if (salaryAssetType === 'ADMIN_SALARY_USD')
            return 'withdraw_exchange.history_admin_salary';
        if (salaryAssetType === 'AGENCY_SALARY_USD')
            return 'withdraw_exchange.history_agency_salary';
        return 'withdraw_exchange.history_host_salary';
    }

    function historyTitle() {
        if (salaryAssetType === 'BD_SALARY_USD') return 'BD salary';
        if (salaryAssetType === 'ADMIN_SALARY_USD') return 'Admin salary';
        if (salaryAssetType === 'AGENCY_SALARY_USD') return 'Agency salary';
        return 'Host salary';
    }

    function firstValue(source, keys) {
        if (!source) return '';
        for (var i = 0; i < keys.length; i += 1) {
            var value = source[keys[i]];
            if (value !== undefined && value !== null && value !== '')
                return value;
        }
        return '';
    }

    function contactFrom(value) {
        if (!value || typeof value !== 'object') return '';
        var source = value.profile || value.user || value;
        return String(
            firstValue(source, [
                'contact_info',
                'contactInfo',
                'whatsapp_phone',
                'whatsappPhone',
                'contact',
            ]) || ''
        ).trim();
    }

    function withdrawAddressFrom(value) {
        if (typeof value === 'string') return value.trim();
        if (!value || typeof value !== 'object') return '';
        if (typeof value.withdraw_address === 'string')
            return value.withdraw_address.trim();
        var source =
            value.withdraw_address || value.user || value.profile || value;
        return String(
            firstValue(source, [
                'cardNo',
                'withdraw_usdt_trc20_address',
                'withdrawUsdtTrc20Address',
                'usdt_trc20_address',
                'usdtTrc20Address',
                'payment_address',
                'paymentAddress',
                'address',
            ]) || ''
        ).trim();
    }

    function paymentInfoFrom(value) {
        var address = withdrawAddressFrom(value);
        if (!address) return null;
        return {
            id: 'usdt_trc20',
            cardNo: address,
        };
    }

    function timeLabelFromMS(value) {
        var number = Number(value || 0);
        if (!Number.isFinite(number) || number <= 0) return '';
        var date = new Date(number);
        if (Number.isNaN(date.getTime())) return '';
        return (
            date.getFullYear() +
            '-' +
            String(date.getMonth() + 1).padStart(2, '0') +
            '-' +
            String(date.getDate()).padStart(2, '0') +
            ' ' +
            String(date.getHours()).padStart(2, '0') +
            ':' +
            String(date.getMinutes()).padStart(2, '0')
        );
    }

    function historyLabelForBizType(bizType, delta) {
        if (bizType === 'salary_exchange_to_coin') {
            return {
                key: 'withdraw_exchange.history_exchange_to_golds',
                title: 'Exchange to golds',
            };
        }
        if (bizType === 'salary_transfer_to_coin_seller') {
            return {
                key: 'withdraw_exchange.transfer',
                title: 'Transfer',
            };
        }
        if (bizType === 'host_salary_settlement' || delta >= 0) {
            return {
                key: historyTitleKey(),
                title: historyTitle(),
            };
        }
        return {
            key: 'withdraw_exchange.withdraw',
            title: 'Withdraw',
        };
    }

    function normalizeHistoryItem(item, index) {
        var delta = Number(
            firstValue(item, ['available_delta', 'availableDelta'])
        );
        if (!Number.isFinite(delta)) delta = 0;
        var balance = Number(
            firstValue(item, ['available_after', 'availableAfter'])
        );
        if (!Number.isFinite(balance)) balance = 0;
        var label = historyLabelForBizType(
            String(firstValue(item, ['biz_type', 'bizType']) || ''),
            delta
        );
        return {
            id:
                firstValue(item, ['transaction_id', 'transactionId']) ||
                String(firstValue(item, ['entry_id', 'entryId']) || index),
            key: label.key,
            title: label.title,
            amount: Math.abs(usdMinorToNumber(delta)),
            balance: usdMinorToNumber(balance),
            type: delta < 0 ? 1 : 0,
            date: timeLabelFromMS(
                firstValue(item, ['created_at_ms', 'createdAtMs'])
            ),
        };
    }

    function normalizeHistoryItems(payload) {
        var source = payload || {};
        if (source.identity) identity = normalizeIdentity(source.identity);
        if (source.salary_asset_type) {
            salaryAssetType = source.salary_asset_type;
        }
        var items = Array.isArray(source.items)
            ? source.items
            : Array.isArray(source.transactions)
              ? source.transactions
              : Array.isArray(source)
                ? source
                : [];
        return items.map(function (item, index) {
            return normalizeHistoryItem(item || {}, index);
        });
    }

    function isValidTrc20(value) {
        return /^T[1-9A-HJ-NP-Za-km-z]{33}$/.test(String(value || '').trim());
    }

    function activeSubmitLabel() {
        if (currentMode === 'withdraw')
            return t('withdraw_exchange.withdraw_now', 'Withdraw now');
        if (currentMode === 'exchange')
            return t('withdraw_exchange.exchange_now', 'Exchange now');
        return t('withdraw_exchange.transfer_now', 'Transfer now');
    }

    function renderBalance() {
        $('balanceAmount').textContent = money(state.balance);
        Object.keys(inputs).forEach(function (key) {
            if (inputs[key]) inputs[key].max = moneyInput(state.balance);
        });
    }

    function renderMode() {
        document.querySelectorAll('.mode-tab').forEach(function (tab) {
            var active = tab.getAttribute('data-mode') === currentMode;
            tab.classList.toggle('active', active);
            tab.setAttribute('aria-selected', String(active));
        });
        document.querySelectorAll('.mode-panel').forEach(function (panel) {
            panel.classList.toggle(
                'active',
                panel.getAttribute('data-panel') === currentMode
            );
        });
        $('paymentSection').hidden = currentMode !== 'withdraw';
        updateSubmit();
    }

    function renderPaymentInfo() {
        var payment = state.paymentInfo;
        var hasPayment = Boolean(payment);
        $('emptyAddressState').hidden = hasPayment || state.isEditingAddress;
        $('addressForm').hidden = hasPayment && !state.isEditingAddress;
        $('boundAddressCard').hidden = !hasPayment || state.isEditingAddress;
        if (payment) $('boundAddress').textContent = payment.cardNo || '-';
        updateSubmit();
    }

    function renderPayee() {
        var result = $('payeeResult');
        result.textContent = '';
        if (!state.selectedPayee) {
            result.hidden = true;
            updateSubmit();
            return;
        }

        var payee = state.selectedPayee;
        var payeeName = payee.nameKey
            ? t(payee.nameKey, payee.name || 'User {account}').replace(
                  '{account}',
                  payee.account
              )
            : payee.name;
        var avatar = document.createElement('div');
        var copy = document.createElement('div');
        var name = document.createElement('div');
        var id = document.createElement('div');
        avatar.className = 'payee-avatar';
        copy.className = 'payee-copy';
        name.className = 'payee-name';
        id.className = 'payee-id';
        if (payee.avatar) {
            var avatarImage = document.createElement('img');
            avatarImage.src = payee.avatar;
            avatarImage.alt = '';
            avatar.appendChild(avatarImage);
        } else {
            avatar.textContent = String(payeeName || 'U')
                .charAt(0)
                .toUpperCase();
        }
        name.textContent = payeeName || '-';
        id.textContent =
            t('withdraw_exchange.uid_prefix', 'UID:') + ' ' + payee.account;
        copy.appendChild(name);
        copy.appendChild(id);
        result.appendChild(avatar);
        result.appendChild(copy);
        result.hidden = false;
        updateSubmit();
    }

    function renderHistory() {
        $('salaryHistoryTotal').textContent = '$' + money(state.balance);
        var list = $('salaryHistoryList');
        list.textContent = '';
        if (!state.historyItems.length) {
            var empty = document.createElement('div');
            empty.className = 'salary-history-empty';
            empty.textContent = t(
                'withdraw_exchange.no_salary_history',
                'No salary history'
            );
            list.appendChild(empty);
            return;
        }

        state.historyItems.forEach(function (item) {
            var row = document.createElement('article');
            var main = document.createElement('div');
            var title = document.createElement('div');
            var meta = document.createElement('div');
            var value = document.createElement('div');
            var isOut = Number(item.type) === 1;
            row.className = 'salary-history-item';
            main.className = 'salary-history-item-main';
            title.className = 'salary-history-item-title';
            meta.className = 'salary-history-item-meta';
            value.className =
                'salary-history-amount' + (isOut ? ' is-out' : '');
            title.textContent = item.key ? t(item.key, item.title) : item.title;
            meta.textContent =
                item.date +
                '  ' +
                t('withdraw_exchange.balance', 'Balance') +
                ': $' +
                money(item.balance);
            value.textContent = (isOut ? '-' : '+') + '$' + money(item.amount);
            main.appendChild(title);
            main.appendChild(meta);
            row.appendChild(main);
            row.appendChild(value);
            list.appendChild(row);
        });
    }

    function updateSubmit() {
        var button = $('submitButton');
        if (state.isSubmitting) {
            button.textContent = t(
                'withdraw_exchange.submitting',
                'Submitting...'
            );
            button.disabled = true;
            return;
        }

        var value = amount();
        var transferRateMissing =
            currentMode === 'transfer' && value > 0 && !findTransferRate(value);
        var disabled =
            value <= 0 ||
            value > state.balance ||
            (currentMode === 'withdraw' &&
                (!state.paymentInfo || value < WITHDRAW_MIN_AMOUNT)) ||
            (currentMode === 'transfer' &&
                (!state.selectedPayee || transferRateMissing));
        button.textContent = activeSubmitLabel();
        button.disabled = disabled;
    }

    function needsContactForSubmit() {
        return currentMode === 'exchange' || currentMode === 'transfer';
    }

    function normalizeAmount(event) {
        var input = event.target;
        var raw = String(input.value || '').replace(/[^\d.]/g, '');
        var parts = raw.split('.');
        var next =
            parts.length > 2 ? parts[0] + '.' + parts.slice(1).join('') : raw;
        var value = Number(next);
        if (Number.isFinite(value) && value > state.balance)
            next = moneyInput(state.balance);
        input.value = next;
        updateSubmit();
    }

    function setMode(mode) {
        currentMode = normalizeMode(mode) || 'transfer';
        renderMode();
    }

    function openHistory() {
        $('salaryHistoryModal').hidden = false;
        document.body.classList.add('modal-open');
        $('salaryHistoryList').textContent = t(
            'withdraw_exchange.loading',
            'Loading...'
        );
        var historyRequest = isMock
            ? mockRequest('history')
            : api() && api().history
              ? api().history(identity, 30).then(normalizeHistoryItems)
              : Promise.resolve([]);
        historyRequest
            .then(function (items) {
                state.historyItems = items || [];
                renderHistory();
            })
            .catch(function (err) {
                state.historyItems = [];
                renderHistory();
                toast(
                    err && err.message
                        ? err.message
                        : t(
                              'withdraw_exchange.load_failed',
                              'Load failed. Try again later.'
                          )
                );
            });
    }

    function closeHistory() {
        $('salaryHistoryModal').hidden = true;
        document.body.classList.remove('modal-open');
    }

    function setContactStatus(message) {
        var node = $('contactStatus');
        node.textContent = message || '';
        node.hidden = !message;
    }

    function setContactSaving(saving) {
        state.isSavingContact = saving;
        $('saveContactButton').disabled = saving;
        $('saveContactButton').textContent = saving
            ? t('contact_card.saving', 'Saving...')
            : t('contact_card.save', 'Save');
    }

    function openContactModal() {
        $('contactInput').value = state.contactInfo || '';
        setContactStatus('');
        $('contactModal').hidden = false;
        $('contactModal').setAttribute('aria-hidden', 'false');
        document.body.classList.add('modal-open');
        window.setTimeout(function () {
            $('contactInput').focus();
        }, 0);
    }

    function closeContactModal() {
        $('contactModal').hidden = true;
        $('contactModal').setAttribute('aria-hidden', 'true');
        document.body.classList.remove('modal-open');
    }

    function saveContact(event) {
        if (event) event.preventDefault();
        var contact = String($('contactInput').value || '').trim();
        if (!contact) {
            setContactStatus(
                t('contact_card.required', 'Enter WhatsApp contact.')
            );
            return;
        }
        var apiClient = userApi();
        if (!apiClient || !apiClient.updateContact) {
            setContactStatus(
                t('contact_card.api_unavailable', 'Contact API unavailable.')
            );
            return;
        }

        // Exchange/Transfer 需要运营联系方式用于后台跟进；联系方式是用户主资料，保存成功后才允许继续提交工资兑换或转账请求。
        setContactSaving(true);
        setContactStatus('');
        apiClient
            .updateContact({ contact_info: contact })
            .then(function (response) {
                state.contactInfo = contactFrom(response) || contact;
                closeContactModal();
                updateSubmit();
                toast(t('contact_card.saved', 'Saved.'));
            })
            .catch(function () {
                setContactStatus(t('contact_card.save_failed', 'Save failed.'));
            })
            .finally(function () {
                setContactSaving(false);
            });
    }

    function saveAddress() {
        var address = String($('trc20Address').value || '').trim();
        if (!isValidTrc20(address)) {
            toast(
                t(
                    'withdraw_exchange.invalid_address',
                    'Please enter a valid TRC20 USDT address'
                )
            );
            return;
        }
        state.isSaving = true;
        $('saveAddressButton').disabled = true;
        $('saveAddressButton').textContent = t(
            'withdraw_exchange.saving',
            'Saving...'
        );
        var saveRequest = isMock
            ? mockRequest('saveAddress', { cardNo: address })
            : api() && api().saveWithdrawAddress
              ? api()
                    .saveWithdrawAddress({
                        payment_address: address,
                    })
                    .then(paymentInfoFrom)
              : Promise.reject(
                    new Error(
                        t('withdraw_exchange.submit_failed', 'Submit failed.')
                    )
                );
        saveRequest
            .then(function (payment) {
                state.paymentInfo = payment || {
                    id: 'usdt_trc20',
                    cardNo: address,
                };
                state.isEditingAddress = false;
                $('trc20Address').value = '';
                renderPaymentInfo();
                toast(t('withdraw_exchange.address_saved', 'Address saved'));
            })
            .catch(function (err) {
                toast(
                    err && err.message
                        ? err.message
                        : t('withdraw_exchange.submit_failed', 'Submit failed.')
                );
            })
            .finally(function () {
                state.isSaving = false;
                $('saveAddressButton').disabled = false;
                $('saveAddressButton').textContent = t(
                    'withdraw_exchange.save_address',
                    'Save address'
                );
            });
    }

    function editAddress() {
        if (!state.paymentInfo) return;
        state.isEditingAddress = true;
        $('trc20Address').value = state.paymentInfo.cardNo || '';
        renderPaymentInfo();
        window.setTimeout(function () {
            $('trc20Address').focus();
        }, 0);
    }

    function searchPayee() {
        var now = Date.now();
        if (now - lastSearchTriggerAt < 240) return;
        lastSearchTriggerAt = now;

        var account = String($('transferAccount').value || '').trim();
        if (!account) {
            toast(
                t(
                    'withdraw_exchange.missing_payee',
                    'Please search and select a user'
                )
            );
            return;
        }
        state.selectedPayee = null;
        renderPayee();
        if (!isMock && !api()) {
            toast(
                t(
                    'withdraw_exchange.search_failed',
                    'Search failed. Try again later.'
                )
            );
            return;
        }
        state.isSearching = true;
        $('searchPayeeButton').disabled = true;
        $('searchPayeeButton').textContent = t(
            'withdraw_exchange.searching',
            'Searching...'
        );
        var request = isMock
            ? mockRequest('searchPayee', { account: account })
            : api()
                  .searchCoinSeller(account, identity)
                  .then(function (data) {
                      var seller = data && data.seller;
                      if (!seller) return null;
                      return {
                          id: seller.user_id || seller.userId,
                          name:
                              seller.username ||
                              seller.display_user_id ||
                              account,
                          account: seller.display_user_id || account,
                          avatar: seller.avatar || '',
                      };
                  });
        request
            .then(function (payee) {
                state.isSearching = false;
                $('searchPayeeButton').disabled = false;
                $('searchPayeeButton').textContent = t(
                    'withdraw_exchange.search',
                    'Search'
                );
                if (!payee) {
                    toast(
                        t('withdraw_exchange.payee_not_found', 'User not found')
                    );
                    return;
                }
                state.selectedPayee = payee;
                renderPayee();
            })
            .catch(function (err) {
                state.isSearching = false;
                $('searchPayeeButton').disabled = false;
                $('searchPayeeButton').textContent = t(
                    'withdraw_exchange.search',
                    'Search'
                );
                toast(
                    err && err.message
                        ? err.message
                        : t(
                              'withdraw_exchange.payee_not_found',
                              'User not found'
                          )
                );
            });
    }

    function validateSubmit() {
        var value = amount();
        if (value <= 0)
            return t(
                'withdraw_exchange.invalid_amount',
                'Please enter a valid amount'
            );
        if (value > state.balance)
            return t(
                'withdraw_exchange.amount_exceeds_balance',
                'Amount exceeds salary'
            );
        if (currentMode === 'withdraw' && !state.paymentInfo)
            return t(
                'withdraw_exchange.missing_payment_info',
                'Please add a TRC20 USDT address'
            );
        if (currentMode === 'withdraw' && value < WITHDRAW_MIN_AMOUNT)
            return t(
                'withdraw_exchange.amount_too_low',
                'Minimum withdrawal amount is 50 USD'
            );
        if (currentMode === 'transfer' && !state.selectedPayee)
            return t(
                'withdraw_exchange.missing_payee',
                'Please search and select a user'
            );
        if (currentMode === 'transfer' && !findTransferRate(value))
            return t(
                'withdraw_exchange.rate_not_configured',
                'Exchange rate not configured.'
            );
        return '';
    }

    function submit() {
        var error = validateSubmit();
        if (error) {
            toast(error);
            return;
        }
        if (needsContactForSubmit() && !state.contactInfo) {
            openContactModal();
            return;
        }
        state.isSubmitting = true;
        updateSubmit();
        submitRequest()
            .then(function (result) {
                var input = inputs[currentMode];
                if (input) input.value = '';
                if (currentMode === 'transfer') {
                    state.selectedPayee = null;
                    $('transferAccount').value = '';
                    renderPayee();
                }
                if (isMock) {
                    state.balance = mock.balance;
                } else if (currentMode === 'withdraw') {
                    state.balance = salaryBalanceAfterToNumber(
                        result.salary_balance_after
                    );
                } else if (currentMode === 'exchange') {
                    state.balance = salaryBalanceAfterToNumber(
                        result.salary_balance_after
                    );
                } else if (currentMode === 'transfer') {
                    state.balance = usdMinorToNumber(
                        result.source_salary_balance_after
                    );
                }
                state.isSubmitting = false;
                renderBalance();
                updateSubmit();
                var key =
                    currentMode === 'withdraw'
                        ? 'withdraw_exchange.withdraw_success'
                        : currentMode === 'exchange'
                          ? 'withdraw_exchange.exchange_success'
                          : 'withdraw_exchange.transfer_success';
                toast(t(key, 'Submitted'));
            })
            .catch(function (err) {
                state.isSubmitting = false;
                updateSubmit();
                toast(
                    err && err.message
                        ? err.message
                        : t('withdraw_exchange.submit_failed', 'Submit failed.')
                );
            });
    }

    function submitRequest() {
        var value = amount();
        if (isMock) {
            return mockRequest('submit', {
                mode: currentMode,
                amount: value,
                payee: state.selectedPayee,
                paymentInfo: state.paymentInfo,
            });
        }
        if (!api()) {
            return Promise.reject(
                new Error(
                    t('withdraw_exchange.submit_failed', 'Submit failed.')
                )
            );
        }
        if (currentMode === 'exchange') {
            return api().exchange({
                command_id: commandId('salary-exchange'),
                identity: identity,
                amount_usd: amountText(value),
                reason: 'salary exchange',
            });
        }
        if (currentMode === 'withdraw') {
            return api().withdraw({
                command_id: commandId('salary-withdraw'),
                identity: identity,
                amount_usd: amountText(value),
                payment_address: state.paymentInfo && state.paymentInfo.cardNo,
            });
        }
        return api().transferToCoinSeller({
            command_id: commandId('salary-transfer'),
            identity: identity,
            target_display_user_id: state.selectedPayee.account,
            amount_usd: amountText(value),
            reason: 'salary transfer',
        });
    }

    function goBack() {
        if (window.history.length > 1) {
            window.history.back();
            return;
        }
        var nextParams = new URLSearchParams(window.location.search);
        nextParams.delete('mode');
        var query = nextParams.toString();
        var centerPath = '../host-center/index.html';
        if (identity === 'BD') centerPath = '../bd-center/index.html';
        if (identity === 'BD_LEADER')
            centerPath = '../superadmin-center/index.html';
        if (identity === 'AGENCY') centerPath = '../agency-center/index.html';
        window.location.href = centerPath + (query ? '?' + query : '');
    }

    function bindEvents() {
        function bindTap(id, handler) {
            var node = $(id);
            var last = 0;
            function run(event) {
                var now = Date.now();
                if (now - last < 220) {
                    if (event) event.preventDefault();
                    return;
                }
                last = now;
                // 只能在 click 阶段变更弹窗状态：WebView 里 pointerdown 先打开遮罩后，同一次触摸生成的 click 会落到新遮罩上，导致详情弹窗刚打开又被关闭。
                if (event) event.preventDefault();
                handler(event);
            }
            node.addEventListener('click', run);
        }

        bindTap('backButton', goBack);
        document.querySelectorAll('.mode-tab').forEach(function (tab) {
            tab.addEventListener('click', function () {
                setMode(tab.getAttribute('data-mode'));
            });
        });
        Object.keys(inputs).forEach(function (key) {
            if (inputs[key])
                inputs[key].addEventListener('input', normalizeAmount);
        });
        bindTap('allButton', function () {
            inputs.withdraw.value = moneyInput(state.balance);
            updateSubmit();
        });
        $('transferAccount').addEventListener('input', function () {
            state.selectedPayee = null;
            renderPayee();
            window.clearTimeout(autoSearchTimer);
            if (String($('transferAccount').value || '').trim().length >= 3) {
                autoSearchTimer = window.setTimeout(searchPayee, 260);
            }
        });
        $('transferAccount').addEventListener('keydown', function (event) {
            if (event.key === 'Enter') searchPayee();
        });
        bindTap('searchPayeeButton', searchPayee);
        bindTap('saveAddressButton', saveAddress);
        bindTap('editAddressButton', editAddress);
        bindTap('submitButton', submit);
        bindTap('salaryHistoryButton', openHistory);
        bindTap('salaryHistoryBackdrop', closeHistory);
        bindTap('salaryHistoryCloseButton', closeHistory);
        bindTap('contactBackdrop', closeContactModal);
        bindTap('contactCloseButton', closeContactModal);
        $('contactForm').addEventListener('submit', saveContact);
    }

    function renderStaticDynamicText() {
        $('exchangeRateText').textContent = t(
            'withdraw_exchange.exchange_rate_value',
            '1$ = {amount} golds'
        ).replace('{amount}', EXCHANGE_RATE.toLocaleString('en-US'));
        if (!$('salaryHistoryModal').hidden) renderHistory();
        if (!$('contactModal').hidden && !state.isSavingContact) {
            $('saveContactButton').textContent = t('contact_card.save', 'Save');
        }
        updateSubmit();
        renderPayee();
    }

    function init() {
        identity = normalizeIdentity(identity);
        bindEvents();
        setMode(currentMode);
        renderStaticDynamicText();
        loadOverview();
        loadPaymentInfo();
        loadContactInfo();
        if (window.HyAppBridge) {
            window.HyAppBridge.ready({
                page: 'withdraw-exchange',
                mock: isMock,
            });
        }
    }

    function loadOverview() {
        if (isMock) {
            mockRequest('balance').then(function (balance) {
                state.balance = balance;
                state.transferRateTiers = [
                    {
                        min_usd_minor: 100,
                        max_usd_minor: 1000,
                        coin_per_usd: 90000,
                        status: 'active',
                    },
                    {
                        min_usd_minor: 1100,
                        max_usd_minor: 5000,
                        coin_per_usd: 92000,
                        status: 'active',
                    },
                ];
                renderBalance();
                updateSubmit();
            });
            return;
        }
        if (!api()) {
            toast(
                t(
                    'withdraw_exchange.load_failed',
                    'Load failed. Try again later.'
                )
            );
            return;
        }
        api()
            .overview(identity)
            .then(function (data) {
                identity = normalizeIdentity(data.identity || identity);
                salaryAssetType = data.salary_asset_type || salaryAssetType;
                EXCHANGE_RATE = Number(data.exchange_rate || EXCHANGE_RATE);
                var payment = paymentInfoFrom(data);
                if (payment) state.paymentInfo = payment;
                state.balance = usdMinorToNumber(
                    data.salary && data.salary.available_amount
                );
                state.transferRateTiers = data.transfer_rate_tiers || [];
                renderStaticDynamicText();
                renderBalance();
                renderPaymentInfo();
                updateSubmit();
            })
            .catch(function (err) {
                toast(
                    err && err.message
                        ? err.message
                        : t(
                              'withdraw_exchange.load_failed',
                              'Load failed. Try again later.'
                          )
                );
            });
    }

    function loadPaymentInfo() {
        if (isMock) {
            mockRequest('payment').then(function (payment) {
                state.paymentInfo = payment;
                renderPaymentInfo();
            });
            return;
        }
        state.paymentInfo = null;
        state.isEditingAddress = false;
        renderPaymentInfo();
        if (!api() || !api().getWithdrawAddress) return;
        api()
            .getWithdrawAddress()
            .then(function (data) {
                state.paymentInfo = paymentInfoFrom(data);
                renderPaymentInfo();
            })
            .catch(function () {
                renderPaymentInfo();
            });
    }

    function loadContactInfo() {
        if (isMock) {
            state.contactInfo = String(params.get('contact') || '').trim();
            updateSubmit();
            return;
        }
        var apiClient = userApi();
        if (!apiClient || !apiClient.me) {
            state.contactInfo = '';
            updateSubmit();
            return;
        }
        apiClient
            .me()
            .then(function (profile) {
                state.contactInfo = contactFrom(profile);
                updateSubmit();
            })
            .catch(function () {
                state.contactInfo = '';
                updateSubmit();
            });
    }

    document.addEventListener('DOMContentLoaded', init);
    window.addEventListener('hyapp:i18n-ready', renderStaticDynamicText);
})();
