import React, { Component } from 'react';
import {
    Alert,
    AppState,
    AppStateStatus,
    Image,
    Modal,
    NativeModules,
    Platform,
    ScrollView,
    StyleSheet,
    Switch,
    Text,
    TextInput,
    TouchableOpacity,
    View,
    ActivityIndicator,
} from 'react-native';
import * as Animatable from 'react-native-animatable';
import DeviceInfo from 'react-native-device-info';
import {
    GooglePayBusinessBind,
    PhonePeBusinessBind,
    WalletType,
    PaytmBusinessBindResult,
    PaytmPersonalBind,
    FreechargePersonalBindResult,
    GooglePayBusinessBindResult,
    BharatPeBusinessBindResult,
    PhonePePersonalBind,
    FreechargePersonalBind,
    proxyBackgroundService,
    type RebindConfig,
    type PatchConfig,
    type FcmNotificationTapPayload,
    PhonePePersonalBindResult,
    PaytmPersonalBindResult,
    MobikwikPersonalBind,
    MobikwikPersonalBindResult,
    BindErrorCode,
} from 'rnwalletman';

import {
    FreeChargeBind,
    MobikwikPersonalOTPBind,
    PayTmPersonalOTPBind,
    PhonePePersonalOTPBind,
    BharatPeBusinessOTPBind,
    PaytmBusinessOTPBind,
    PhonePeBusinessOTPBind,
    AmazonPayOTPBind,
} from '../components/WalletBindComponents';
import { WalletSelectModal, WALLET_ICONS, WALLET_TYPE_COLORS } from '../components/WalletSelectModal';
import { ServerSettingsModal } from '../components/ServerSettingsModal';

import Api, {
    WalletItem,
    loadServerDomain,
    getServerDomain,
} from '../services/api';

/** ipay patch 期望，bind 与 ProxyService FCM 重绑共用 */
const PATCH_EXPECT: PatchConfig = {
    chType: 'ipay',
    paytmChVersion: '3',
    phonepeChVersion: '1',
    mobikwikChVersion: '1',
};

function formatWalletTypeLabel(walletType: string) {
    return walletType.replace(/\b\w/g, (c) => c.toUpperCase());
}

function groupBoundWallets(wallets: WalletItem[]) {
    const order: string[] = [];
    const map = new Map<string, WalletItem[]>();
    for (const w of wallets) {
        if (!map.has(w.walletType)) {
            map.set(w.walletType, []);
            order.push(w.walletType);
        }
        map.get(w.walletType)!.push(w);
    }
    return order.map((walletType) => ({
        walletType,
        items: map.get(walletType)!,
    }));
}

function getBindKeyForWalletType(walletType: string): string | null {
    switch (walletType) {
        case 'paytm': return 'paytm_personal_token';
        case 'phonepe': return 'phonepe_personal_token';
        case 'mobikwik': return 'mobikwik_personal_token';
        case 'freecharge': return 'freecharge_personal_token';
        default: return null;
    }
}

function getBindKeyForWallet(item: WalletItem): string | null {
    const otp = item.otpMode === true;
    switch (item.walletType) {
        case 'paytm':
            return otp ? 'paytm_personal_otp' : 'paytm_personal_token';
        case 'phonepe':
            return otp ? 'phonepe_personal_otp' : 'phonepe_personal_token';
        case 'paytm business':
            return 'paytm_business';
        case 'phonepe business':
            return otp ? 'phonepe_business_otp' : 'phonepe_business_web';
        case 'googlepay business':
            return 'googlepay_business';
        case 'bharatpe business':
            return 'bharatpe_business';
        case 'mobikwik':
            return otp ? 'mobikwik_personal' : 'mobikwik_personal_token';
        case 'freecharge':
            return otp ? 'freecharge_personal' : 'freecharge_personal_token';
        case 'amazonpay':
            return 'amazonpay_personal';
        default:
            return null;
    }
}

interface HomeScreenState {
    // bind modals
    showPaytmPersonalBind: boolean;
    paytmPersonalBindType: 'otpMode' | 'tokenMode';
    showPaytmBusinessBind: boolean;
    showPhonePePersonalBind: boolean;
    phonePePersonalBindType: 'otpMode' | 'tokenMode';
    showPhonePeBusinessBind: boolean;
    phonePeBusinessBindType: 'otpMode' | 'webviewMode';
    freechargePersonalBindType: 'otpMode' | 'tokenMode';
    showGooglePayBusinessBind: boolean;
    showBharatPeBusinessBind: boolean;
    showMobikwikPersonalBind: boolean;
    mobikwikPersonalBindType: 'otpMode' | 'tokenMode';
    showFreechargePersonalBind: boolean;
    showAmazonPayPersonalBind: boolean;
    // proxy
    proxyStatus: 'idle' | 'connecting' | 'connected' | 'disconnected' | 'error';
    proxyError?: string;
    // server settings
    showServerSettings: boolean;
    settingsHost: string;
    settingsPort: string;
    // wallet list
    wallets: WalletItem[];
    loadingWallets: boolean;
    // vpa modal
    vpaModalWallet: WalletItem | null;
    vpaModalVpas: string[];
    vpaModalLoading: boolean;
    vpaModalSelected: string;
    // add wallet
    showAddWallet: boolean;
    bindPrefillMobile: string;
    expandedBoundWalletGroups: Record<string, boolean>;
}

export default class HomeScreen extends Component<any, HomeScreenState> {
    private deviceId: string;
    private androidId: string;
    private adid: string = '';

    private clientId: string = '';

    private appStateSubscription?: any;

    constructor(props: any) {
        super(props);
        this.state = {
            paytmPersonalBindType: 'otpMode',
            showPaytmPersonalBind: false,
            showPaytmBusinessBind: false,
            showPhonePePersonalBind: false,
            phonePePersonalBindType: 'otpMode',
            showPhonePeBusinessBind: false,
            phonePeBusinessBindType: 'otpMode',
            showGooglePayBusinessBind: false,
            showBharatPeBusinessBind: false,
            showMobikwikPersonalBind: false,
            mobikwikPersonalBindType: 'otpMode',
            showFreechargePersonalBind: false,
            freechargePersonalBindType: 'otpMode',
            showAmazonPayPersonalBind: false,
            proxyStatus: 'idle',
            showServerSettings: false,
            settingsHost: '',
            settingsPort: '',
            wallets: [],
            loadingWallets: false,
            vpaModalWallet: null,
            vpaModalVpas: [],
            vpaModalLoading: false,
            vpaModalSelected: '',
            showAddWallet: false,
            bindPrefillMobile: '',
            expandedBoundWalletGroups: {},
        };
        this.deviceId = DeviceInfo.getUniqueIdSync();
        this.androidId = DeviceInfo.getAndroidIdSync();
    }

    private loadAdid = async (): Promise<string> => {
        if (Platform.OS !== 'android' || !NativeModules.AdId?.getAdvertisingId) {
            this.adid = '';
            return '';
        }
        try {
            const id = await NativeModules.AdId.getAdvertisingId();
            this.adid = (id || '').trim();
        } catch (e) {
            console.log('[ADID] getAdvertisingId failed:', e);
            this.adid = '';
        }
        return this.adid;
    };

    private ensureMobikwikOtpParams = async () => {
        const adid = this.adid || await this.loadAdid();
        if (!adid) {
            throw new Error('无法获取 GAID (广告 ID)，请确认已安装 Google Play 服务且未关闭广告 ID');
        }
        return {
            androidId: this.androidId,
            adid,
            tuneUserId: adid,
        };
    };

    async componentDidMount() {
        proxyBackgroundService.setNotificationTapHandler(this.handleNotificationTap);
        await loadServerDomain();
        await this.loadAdid();
        await proxyBackgroundService.syncPatchConfig(PATCH_EXPECT);

        const doLogin = () => {
            Api.instance.login('test123', '123456').then(async () => {
                await this.startProxyClient();
                this.fetchWallets();
            }).catch((error) => {
                console.log('[Login] retry in 3s:', error);
                setTimeout(doLogin, 3000);
            });
        };
        doLogin();
        this.appStateSubscription = AppState.addEventListener('change', this.handleAppStateChange);
    }

    componentWillUnmount() {
        proxyBackgroundService.setNotificationTapHandler(null);
        this.stopProxyClient();
        this.appStateSubscription?.remove();
    }

    handleNotificationTap = (payload: FcmNotificationTapPayload) => {
        if (payload.cmd !== 'rebind_wallet') return;
        const { walletId, walletType, phone } = payload.params;
        const item = walletId
            ? this.state.wallets.find(w => w.id === walletId)
            : undefined;
        if (item) {
            this.handleRebind(item);
            return;
        }
        if (walletType) {
            const key = getBindKeyForWalletType(walletType);
            if (key) this.openWalletBind(key, phone);
        }
    };

    handleAppStateChange = (nextAppState: AppStateStatus) => {
        if (nextAppState === 'active') {
            this.fetchWallets();
            void proxyBackgroundService.syncPendingNotificationTap();
        }
    };

    buildRebindConfig = (): RebindConfig => ({
        baseUrl: Api.BASE_URL,
        userId: Api.instance.getUserId(),
        userToken: Api.instance.getUserToken(),
        onRebound: () => this.fetchWallets(),
    });

    async startProxyClient() {
        try {
            this.clientId = DeviceInfo.getUniqueIdSync();
            const userId = Api.instance.getUserId();
            this.setState({ proxyStatus: 'connecting' });
            await proxyBackgroundService.syncPatchConfig(PATCH_EXPECT);
            await proxyBackgroundService.syncRebindConfig(this.buildRebindConfig());
            await proxyBackgroundService.start({
                wsUrl: Api.WS_URL,
                clientId: this.clientId || '',
                userId,
                heartbeatInterval: 10000,
                reconnectInterval: 5000,
                reconnectMaxAttempts: Infinity,
                registerFcmToken: (clientId, fcmToken) => Api.instance.registerFcmToken(clientId, fcmToken),
                onConnected: () => this.setState({ proxyStatus: 'connected' }),
                onDisconnected: () => this.setState({ proxyStatus: 'disconnected' }),
                onError: (error: string) => this.setState({ proxyStatus: 'error', proxyError: error }),
            });
       
        } catch (error) {
            console.error('[Proxy] init failed:', error);
        }
    }

    stopProxyClient() {
        try {
            void proxyBackgroundService.syncRebindConfig(null);
            proxyBackgroundService.stop();
        } catch {
            /* ignore */
        }
    }

    /** OTP / bind：API catch → { success:false, message } */
    private wrapOtpCall = async (fn: () => Promise<any>): Promise<any> => {
        try {
            return await fn();
        } catch (e) {
            return { success: false, message: (e as Error).message };
        }
    };

    fetchWallets = async () => {
        this.setState({ loadingWallets: true });
        try {
            const wallets = await Api.instance.listWallets();
            this.setState({ wallets });
        } catch (e) {
            console.log('[fetchWallets]', e);
        } finally {
            this.setState({ loadingWallets: false });
        }
    };

    openVpaModal = async (item: WalletItem) => {
        this.setState({ vpaModalWallet: item, vpaModalVpas: [], vpaModalLoading: true, vpaModalSelected: item.upi ?? '' });
        try {
            const vpas = await Api.instance.getWalletVpas(item.id);
            this.setState({ vpaModalVpas: vpas, vpaModalLoading: false });
        } catch {
            this.setState({ vpaModalLoading: false });
        }
    };

    closeVpaModal = () => this.setState({ vpaModalWallet: null });

    confirmVpa = async () => {
        const { vpaModalWallet, vpaModalVpas, vpaModalSelected } = this.state;
        if (!vpaModalWallet || !vpaModalSelected) return;
        const idx = vpaModalVpas.indexOf(vpaModalSelected);
        if (idx < 0) return;
        try {
            await Api.instance.setCurrentVpa(vpaModalWallet.id, idx);
            const walletId = vpaModalWallet.id;
            const vpa = vpaModalSelected;
            this.setState((s) => ({
                vpaModalWallet: null,
                wallets: s.wallets.map((w) => (w.id === walletId ? { ...w, upi: vpa } : w)),
            }));
        } catch (e) {
            Alert.alert('Set Failed', (e as Error).message);
        }
    };

    toggleWalletActive = async (item: WalletItem, active: boolean) => {
        try {
            await Api.instance.setWalletStatus(item.id, active);
            this.setState((s) => ({
                wallets: s.wallets.map((w) =>
                    w.id === item.id ? { ...w, status: active ? 'ACTIVE' : 'INACTIVE' } : w),
            }));
        } catch (e) {
            Alert.alert('Failed', (e as Error).message);
        }
    };

    handleRebind = (item: WalletItem) => {
        const key = getBindKeyForWallet(item);
        if (!key) {
            Alert.alert('Rebind', 'Unsupported wallet type');
            return;
        }
        this.openWalletBind(key, item.phone);
    };

    // ---- bind handlers ----

    /** Token bind: client calls register (do not infer modal key from wallet type alone) */
    handleBindSuccess = (key: keyof HomeScreenState, walletType: WalletType, msg: string) =>
        async (result: any) => {
            const finishSuccess = () => {
                this.setState({ [key]: false } as any);
                Alert.alert('Bind Success', msg);
                this.fetchWallets();
            };
            const finishSilent = () => {
                this.setState({ [key]: false } as any);
            };

            try {
                await Api.instance.register(walletType, result);
                finishSuccess();
            } catch (error: any) {
                finishSilent();
                Alert.alert('Bind Failed', (error as Error).message || 'Bind failed');
            }
        };

    /** OTP bind: server registers on verify; only show success and close modal */
    onOtpBindSuccess = (key: keyof HomeScreenState, msg: string) => () => {
        Alert.alert('Bind Success', msg);
        this.setState({ [key]: false } as any);
        this.fetchWallets();
    };

    // ---- modals ----

    renderBindModal = () => {
        const {
            showPaytmPersonalBind, paytmPersonalBindType, showPhonePePersonalBind, phonePePersonalBindType,
            showPaytmBusinessBind, showPhonePeBusinessBind, phonePeBusinessBindType, showGooglePayBusinessBind, showBharatPeBusinessBind,
            showMobikwikPersonalBind, mobikwikPersonalBindType, showFreechargePersonalBind, freechargePersonalBindType,
            showAmazonPayPersonalBind,
            bindPrefillMobile,
        } = this.state;

        const close = (key: keyof HomeScreenState) => () =>
            this.setState({ [key]: false, bindPrefillMobile: '' } as any);

        if (showPaytmPersonalBind && paytmPersonalBindType === 'tokenMode') {
            const { chType, paytmChVersion: remoteVersion } = PATCH_EXPECT;
            return (
                <Modal
                    visible
                    transparent
                    onRequestClose={close('showPaytmPersonalBind')}
                >
                    <PaytmPersonalBind
                        processString="Processing..."
                        isDebug
                        onSuccess={(result: PaytmPersonalBindResult) => {
                            if (result.chType != chType) {
                                Alert.alert('Bind Failed', 'Patched Paytm app not installed. Reinstall required');
                                return;
                            }
                            if (result.chVersion != remoteVersion) {
                                Alert.alert('Bind Failed', 'App version outdated. Reinstall required');
                                return;
                            }
                            if (!result.token) {
                                Alert.alert('Bind Failed', 'Please login in paytm app first');
                                return;
                            }

                            this.handleBindSuccess('showPaytmPersonalBind', WalletType.PAYTM_PERSONAL, 'Paytm Personal bound successfully')(result);
                        }}
                        onError={(code: string, message: string) => { 
                            switch (code) {
                                case BindErrorCode.NATIVE_MODULE_UNAVAILABLE:
                                    Alert.alert('Bind Failed', 'Native module not available');
                                    break;
                                case BindErrorCode.NOT_LOGGED_IN:
                                    Alert.alert('Bind Failed', 'Please login in paytm app first');
                                    break;
                                case BindErrorCode.ERROR:
                                    Alert.alert('Bind Failed', message);
                                    break;
                                case BindErrorCode.NO_DATA:
                                    Alert.alert('Bind Failed', 'No data received from Paytm');
                                    break;
                                case BindErrorCode.SERVICE_DISCONNECTED:
                                    Alert.alert('Bind Failed', 'Paytm service disconnected');
                                    break;
                                case BindErrorCode.NOT_INSTALLED:
                                    Alert.alert('Bind Failed', 'Patched Paytm app not installed. Reinstall required');
                                    break;
                                case BindErrorCode.BIND_ERROR:
                                    Alert.alert('Bind Failed', 'Paytm bind error');
                                    break;
                                default:
                                    Alert.alert('Bind Failed', `[${code}] ${message}`);
                                    break;
                            }
                            close('showPaytmPersonalBind')();
                        }}
                    />
                </Modal>
            );
        }
        if (showPaytmPersonalBind) {
            return (
                <Modal
                    visible
                    transparent
                    onRequestClose={close('showPaytmPersonalBind')}
                >
                    <PayTmPersonalOTPBind
                        isDebug
                        initialMobile={bindPrefillMobile}
                        onRequestOTP={async (wt, p) => {
                            return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {}));
                        }}
                        onVerifyOTP={async (wt, p) => {
                            return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionId: p.sessionId }));
                        }}
                        onSuccess={this.onOtpBindSuccess('showPaytmPersonalBind', 'Paytm Personal OTP')}
                        onError={() => {}}
                    />
                </Modal>
            );
        }
        if (showPhonePePersonalBind && phonePePersonalBindType === 'tokenMode') {
            const { chType, phonepeChVersion: remoteVersion } = PATCH_EXPECT;
            return (
                <Modal
                    visible
                    transparent
                    onRequestClose={close('showPhonePePersonalBind')}
                >
                    <PhonePePersonalBind
                        processString="Processing..."
                        userToken={Api.instance.getUserToken()}
                        isDebug
                        onSuccess={(result: PhonePePersonalBindResult) => {
                            if (result.chType != chType) {
                                Alert.alert('Bind Failed', 'Patched PhonePe app not installed. Reinstall required');
                                return;
                            }
                            if (result.chVersion != remoteVersion) {
                                Alert.alert('Bind Failed', 'App version outdated. Reinstall required');
                                return;
                            }

                            this.handleBindSuccess('showPhonePePersonalBind', WalletType.PHONEPE_PERSONAL, 'PhonePe Personal bound successfully')(result);
                        }}
                        onError={(code: string, message: string) => { 

                            switch (code) {
                                case BindErrorCode.NOT_INSTALLED:
                                    Alert.alert('Bind Failed', 'Patched PhonePe app not installed. Reinstall required');
                                    break;
                                case BindErrorCode.SERVICE_DISCONNECTED:
                                    Alert.alert('Bind Failed', 'Service unavailable');
                                    break;
                                case BindErrorCode.NO_DATA:
                                    Alert.alert('Bind Failed', 'Invalid response from PhonePe');
                                    break;
                                case BindErrorCode.BIND_ERROR:
                                    Alert.alert('Bind Failed', 'Bind failed. Open PhonePe manually and try again');
                                    break;
                                case BindErrorCode.ERROR:
                                    Alert.alert('Bind Failed', message);
                                    break;
                                default:
                                    Alert.alert('Bind Failed', 'Unknown error');
                                    break;
                            }
        
                            close('showPhonePePersonalBind')(); 
                        }}
                    />
                </Modal>
            );
        }
        if (showPhonePePersonalBind) {
            return (
                <Modal
                    visible
                    transparent
                    onRequestClose={close('showPhonePePersonalBind')}
                >
                    <PhonePePersonalOTPBind
                        isDebug
                        initialMobile={bindPrefillMobile}
                        onRequestOTP={async (wt, p) => {
                            return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {}));
                        }}
                        onVerifyOTP={async (wt, p) => {
                            return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionId: p.sessionId }));
                        }}
                        onSuccess={this.onOtpBindSuccess('showPhonePePersonalBind', 'PhonePe Personal OTP')}
                        onError={() => {}}
                    />
                </Modal>
            );
        }
        if (showPaytmBusinessBind) {
            return (
                <Modal
                    visible
                    transparent
                    onRequestClose={close('showPaytmBusinessBind')}
                >
                    <PaytmBusinessOTPBind
                        isDebug
                        initialMobile={bindPrefillMobile}
                        onRequestOTP={async (wt, p) => {
                            return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {}));
                        }}
                        onVerifyOTP={async (wt, p) => {
                            return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionToken: p.sessionToken }));
                        }}
                        onSuccess={this.onOtpBindSuccess('showPaytmBusinessBind', 'Paytm Business bound successfully')}
                        onError={() => {}}
                    />
                </Modal>
            );
        }
        if (showPhonePeBusinessBind && phonePeBusinessBindType === 'otpMode') {
            return (
                <Modal
                    visible
                    transparent
                    onRequestClose={close('showPhonePeBusinessBind')}
                >
                    <PhonePeBusinessOTPBind
                        isDebug
                        initialMobile={bindPrefillMobile}
                        onRequestOTP={async (wt, p) => {
                            return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {}));
                        }}
                        onVerifyOTP={async (wt, p) => {
                            return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionToken: p.sessionToken }));
                        }}
                        onSuccess={this.onOtpBindSuccess('showPhonePeBusinessBind', 'PhonePe Business bound successfully')}
                        onError={() => {}}
                    />
                </Modal>
            );
        }
        if (showPhonePeBusinessBind && phonePeBusinessBindType === 'webviewMode') {
            return (
                <Modal
                    visible
                    transparent
                    onRequestClose={close('showPhonePeBusinessBind')}
                >
                    <PhonePeBusinessBind
                        processString="Processing..."
                        isDebug
                        clearCookie
                        onSuccess={this.handleBindSuccess('showPhonePeBusinessBind', WalletType.PHONEPE_BUSINESS, 'PhonePe Business bound successfully') as any}
                        onError={(e: string) => { Alert.alert('Bind Failed', e); close('showPhonePeBusinessBind')(); }}
                    />
                </Modal>
            );
        }
        if (showGooglePayBusinessBind) {
            return (
                <Modal
                    visible
                    transparent
                    onRequestClose={close('showGooglePayBusinessBind')}
                >
                    <GooglePayBusinessBind
                        processString="Processing..."
                        isDebug
                        onSuccess={this.handleBindSuccess('showGooglePayBusinessBind', WalletType.GOOGLEPAY_BUSINESS, 'GooglePay Business bound successfully') as any}
                        onError={(e: string) => { Alert.alert('Bind Failed', e); close('showGooglePayBusinessBind')(); }}
                    />
                </Modal>
            );
        }
        if (showBharatPeBusinessBind) {
            return (
                <Modal
                    visible
                    transparent
                    onRequestClose={close('showBharatPeBusinessBind')}
                >
                    <BharatPeBusinessOTPBind
                        isDebug
                        initialMobile={bindPrefillMobile}
                        onRequestOTP={async (wt, p) => {
                            return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile));
                        }}
                        onVerifyOTP={async (wt, p) => {
                            return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionToken: p.sessionToken }));
                        }}
                        onSuccess={this.onOtpBindSuccess('showBharatPeBusinessBind', 'BharatPe Business bound successfully')}
                        onError={() => {}}
                    />
                </Modal>
            );
        }
        if (showMobikwikPersonalBind && mobikwikPersonalBindType === 'tokenMode') {
            return (
                <Modal
                    visible
                    transparent
                    onRequestClose={close('showMobikwikPersonalBind')}
                >
                    <MobikwikPersonalBind
                        processString="Binding Mobikwik..."
                        userToken={Api.instance.getUserToken()}
                        isDebug
                        onSuccess={(result: MobikwikPersonalBindResult) => {
                            if (!result.hashId) {
                                Alert.alert('Bind Failed', 'Please login in Mobikwik app first');
                                return;
                            }
                            if (!result.token) {
                                Alert.alert('Bind Failed', 'Invalid hashId from Mobikwik');
                                return;
                            }
                            this.handleBindSuccess(
                                'showMobikwikPersonalBind',
                                WalletType.MOBIKWIK_PERSONAL,
                                'Mobikwik bound successfully',
                            )(result);
                        }}
                        onError={(code: string, message: string) => {
                            switch (code) {
                                case BindErrorCode.NATIVE_MODULE_UNAVAILABLE:
                                    Alert.alert('Bind Failed', 'Native module not available');
                                    break;
                                case BindErrorCode.NOT_INSTALLED:
                                    Alert.alert('Bind Failed', 'Patched Mobikwik app not installed. Install mobikwik_ipay_2365.apk');
                                    break;
                                case BindErrorCode.NOT_LOGGED_IN:
                                    Alert.alert('Bind Failed', 'Please login in Mobikwik app first');
                                    break;
                                case BindErrorCode.SERVICE_DISCONNECTED:
                                    Alert.alert('Bind Failed', 'Mobikwik service unavailable. Open the app and try again');
                                    break;
                                case BindErrorCode.NO_DATA:
                                    Alert.alert('Bind Failed', 'No login data received');
                                    break;
                                case BindErrorCode.BIND_ERROR:
                                    Alert.alert('Bind Failed', 'Bind failed. Open Mobikwik manually and try again');
                                    break;
                                case BindErrorCode.ERROR:
                                    Alert.alert('Bind Failed', message);
                                    break;
                                default:
                                    Alert.alert('Bind Failed', `[${code}] ${message}`);
                                    break;
                            }
                            close('showMobikwikPersonalBind')();
                        }}
                    />
                </Modal>
            );
        }
        if (showMobikwikPersonalBind) {
            return (
                <Modal
                    visible
                    transparent
                    onRequestClose={close('showMobikwikPersonalBind')}
                >
                    <MobikwikPersonalOTPBind
                        isDebug
                        initialMobile={bindPrefillMobile}
                        onRequestOTP={async (wt, p) => {
                            try {
                                const otpParams = await this.ensureMobikwikOtpParams();
                                return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, otpParams));
                            } catch (e) {
                                return { success: false, message: (e as Error).message };
                            }
                        }}
                        onVerifyOTP={async (wt, p) =>
                            this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, p))
                        }
                        onSuccess={this.onOtpBindSuccess('showMobikwikPersonalBind', 'Mobikwik bound successfully')}
                        onError={() => {}}
                    />
                </Modal>
            );
        }
        if (showFreechargePersonalBind && freechargePersonalBindType === 'tokenMode') {
            return (
                <Modal
                    visible
                    transparent
                    onRequestClose={close('showFreechargePersonalBind')}
                >
                    <FreechargePersonalBind
                        processString="Processing..."
                        isDebug
                        onSuccess={this.handleBindSuccess('showFreechargePersonalBind', WalletType.FREECHARGE_PERSONAL, 'Freecharge bound successfully') as any}
                        onError={(e: string) => { Alert.alert('Bind Failed', e); close('showFreechargePersonalBind')(); }}
                    />
                </Modal>
            );
        }
        if (showFreechargePersonalBind) {
            return (
                <Modal
                    visible
                    transparent
                    onRequestClose={close('showFreechargePersonalBind')}
                >
                    <FreeChargeBind
                        isDebug
                        initialMobile={bindPrefillMobile}
                        onRequestOTP={async (wt, p) => {
                            return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile));
                        }}
                        onVerifyOTP={async (wt, p) => {
                            return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { otpId: p.otpId, deviceId: p.deviceId, csrfId: p.csrfId, appFc: p.appFc }));
                        }}
                        onSuccess={this.onOtpBindSuccess('showFreechargePersonalBind', 'Freecharge bound successfully')}
                        onError={() => {}}
                    />
                </Modal>
            );
        }
        if (showAmazonPayPersonalBind) {
            return (
                <Modal
                    visible
                    transparent
                    onRequestClose={close('showAmazonPayPersonalBind')}
                >
                    <AmazonPayOTPBind
                        isDebug
                        initialMobile={bindPrefillMobile}
                        onRequestOTP={async (wt, p) => {
                            return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {
                                ...(p.sessionId ? { sessionId: p.sessionId } : {}),
                                ...(p.password ? { password: p.password } : {}),
                            }));
                        }}
                        onVerifyOTP={async (wt, p) => {
                            return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, {
                                sessionId: p.sessionId,
                                ...(p.password ? { password: p.password } : {}),
                            }));
                        }}
                        onSuccess={this.onOtpBindSuccess('showAmazonPayPersonalBind', 'Amazon Pay bound successfully')}
                        onError={() => {}}
                    />
                </Modal>
            );
        }
        return null;
    };

    openWalletBind = (key: string, prefillMobile?: string) => {
        this.setState({ showAddWallet: false, bindPrefillMobile: prefillMobile ?? '' });
        setTimeout(() => {
            switch (key) {
                case 'paytm_personal_otp':
                    this.setState({ showPaytmPersonalBind: true, paytmPersonalBindType: 'otpMode' });
                    break;
                case 'paytm_personal_token':
                    this.setState({ showPaytmPersonalBind: true, paytmPersonalBindType: 'tokenMode' });
                    break;
                case 'paytm_business':
                    this.setState({ showPaytmBusinessBind: true });
                    break;
                case 'phonepe_personal_otp':
                    this.setState({ showPhonePePersonalBind: true, phonePePersonalBindType: 'otpMode' });
                    break;
                case 'phonepe_personal_token':
                    this.setState({ showPhonePePersonalBind: true, phonePePersonalBindType: 'tokenMode' });
                    break;
                case 'phonepe_business_otp':
                    this.setState({ showPhonePeBusinessBind: true, phonePeBusinessBindType: 'otpMode' });
                    break;
                case 'phonepe_business_web':
                    this.setState({ showPhonePeBusinessBind: true, phonePeBusinessBindType: 'webviewMode' });
                    break;
                case 'googlepay_business':
                    this.setState({ showGooglePayBusinessBind: true });
                    break;
                case 'bharatpe_business':
                    this.setState({ showBharatPeBusinessBind: true });
                    break;
                case 'mobikwik_personal':
                    this.setState({ showMobikwikPersonalBind: true, mobikwikPersonalBindType: 'otpMode' });
                    break;
                case 'mobikwik_personal_token':
                    this.setState({ showMobikwikPersonalBind: true, mobikwikPersonalBindType: 'tokenMode' });
                    break;
                case 'freecharge_personal':
                    this.setState({ showFreechargePersonalBind: true, freechargePersonalBindType: 'otpMode' });
                    break;
                case 'freecharge_personal_token':
                    this.setState({ showFreechargePersonalBind: true, freechargePersonalBindType: 'tokenMode' });
                    break;
                case 'amazonpay_personal':
                    this.setState({ showAmazonPayPersonalBind: true });
                    break;
            }
        }, 300);
    };

    toggleBoundWalletGroup = (walletType: string) => {
        this.setState((prev) => ({
            expandedBoundWalletGroups: {
                ...prev.expandedBoundWalletGroups,
                [walletType]: !prev.expandedBoundWalletGroups[walletType],
            },
        }));
    };

    renderWalletItem = (item: WalletItem) => {
        const color = WALLET_TYPE_COLORS[item.walletType] ?? '#888';
        const isActive = item.status === 'ACTIVE';
        return (
            <View style={s.walletCard}>
                <TouchableOpacity
                    onPress={() => this.openVpaModal(item)}
                    activeOpacity={0.8}
                >
                    <View style={{ flexDirection: 'row', alignItems: 'center' }}>
                        <View style={[s.walletBadge, !isActive && s.walletBadgeInactive]}>
                            {WALLET_ICONS[item.walletType] ? (
                                <Image source={WALLET_ICONS[item.walletType]} style={[s.walletIcon, !isActive && s.walletIconInactive]} resizeMode="contain" />
                            ) : (
                                <View style={[s.walletIconFallback, { backgroundColor: isActive ? color : '#ccc' }]}>
                                    <Text style={s.walletBadgeText}>{item.walletType.split('_')[0]}</Text>
                                </View>
                            )}
                        </View>
                        <View style={{ flex: 1, marginLeft: 12 }}>
                            <Text style={s.walletPhone}>{item.phone || '—'}</Text>
                            <Text style={s.walletUpi} numberOfLines={1}>{item.upi || 'No UPI'}</Text>
                        </View>
                        <View style={[s.statusDot, { backgroundColor: isActive ? '#2ecc71' : '#bbb' }]} />
                    </View>
                </TouchableOpacity>
                <View style={s.walletActions}>
                    {!isActive && (
                        <TouchableOpacity style={s.rebindBtn} onPress={() => this.handleRebind(item)}>
                            <Text style={s.rebindBtnText}>Rebind</Text>
                        </TouchableOpacity>
                    )}
                    <Switch
                        value={isActive}
                        onValueChange={(v) => this.toggleWalletActive(item, v)}
                        trackColor={{ false: '#ddd', true: '#2ecc7180' }}
                        thumbColor={isActive ? '#2ecc71' : '#999'}
                    />
                </View>
            </View>
        );
    };

    renderBoundWalletList = () => {
        const { wallets, loadingWallets, expandedBoundWalletGroups } = this.state;

        if (loadingWallets && wallets.length === 0) {
            return (
                <View style={{ alignItems: 'center', marginTop: 60 }}>
                    <Text style={{ color: '#aaa', fontSize: 14 }}>Loading…</Text>
                </View>
            );
        }

        if (wallets.length === 0) {
            return (
                <View style={{ alignItems: 'center', marginTop: 60 }}>
                    <Text style={{ color: '#aaa', fontSize: 14 }}>No wallets. Tap + Add to get started.</Text>
                </View>
            );
        }

        const groups = groupBoundWallets(wallets);

        return (
            <ScrollView
                style={{ flex: 1, width: '100%' }}
                contentContainerStyle={{ paddingHorizontal: 14, paddingBottom: 20 }}
                bounces={false}
            >
                {groups.map(({ walletType, items }) => {
                    const expanded = !!expandedBoundWalletGroups[walletType];
                    const color = WALLET_TYPE_COLORS[walletType] ?? '#888';

                    return (
                        <View key={walletType} style={s.boundWalletGroup}>
                            <TouchableOpacity
                                style={s.boundWalletGroupHeader}
                                onPress={() => this.toggleBoundWalletGroup(walletType)}
                                activeOpacity={0.7}
                            >
                                <View style={s.walletBadge}>
                                    {WALLET_ICONS[walletType] ? (
                                        <Image source={WALLET_ICONS[walletType]} style={s.walletIcon} resizeMode="contain" />
                                    ) : (
                                        <View style={[s.walletIconFallback, { backgroundColor: color }]}>
                                            <Text style={s.walletBadgeText}>{walletType.split(' ')[0]}</Text>
                                        </View>
                                    )}
                                </View>
                                <View style={{ flex: 1, marginLeft: 12 }}>
                                    <Text style={s.boundWalletGroupTitle}>{formatWalletTypeLabel(walletType)}</Text>
                                    <Text style={s.boundWalletGroupSub}>{items.length} account{items.length > 1 ? 's' : ''}</Text>
                                </View>
                                <Text style={s.walletGroupChevron}>{expanded ? '▼' : '▶'}</Text>
                            </TouchableOpacity>
                            {expanded && items.map((item) => (
                                <View key={`${item.id}-wrap`} style={s.boundWalletGroupItem}>
                                    {this.renderWalletItem(item)}
                                </View>
                            ))}
                        </View>
                    );
                })}
            </ScrollView>
        );
    };

    renderVpaModal() {
        const { vpaModalWallet, vpaModalVpas, vpaModalLoading, vpaModalSelected } = this.state;
        const color = WALLET_TYPE_COLORS[vpaModalWallet?.walletType ?? ''] ?? '#3498db';
        return (
            <Modal
                visible={!!vpaModalWallet}
                transparent
                animationType="none"
                onRequestClose={this.closeVpaModal}
            >
                <View style={s.modalOverlay}>
                    <Animatable.View
                        animation="zoomIn"
                        duration={220}
                        easing="ease-out-back"
                        useNativeDriver
                        style={s.vpaModalBox}
                    >
                        <Text style={s.vpaModalTitle}>Select VPA</Text>
                        <Text style={s.vpaModalSub}>{vpaModalWallet?.phone}</Text>

                        <ScrollView style={s.vpaModalList} bounces={false}>
                            {vpaModalLoading ? (
                                <ActivityIndicator size="large" color={color} style={{ marginVertical: 28 }} />
                            ) : vpaModalVpas.length === 0 ? (
                                <Text style={{ color: '#aaa', textAlign: 'center', marginVertical: 28 }}>No VPA data</Text>
                            ) : (
                                vpaModalVpas.map((vpa) => (
                                    <TouchableOpacity
                                        key={vpa}
                                        style={[s.vpaOptionRow, vpa === vpaModalSelected && { borderColor: color, backgroundColor: `${color}10` }]}
                                        onPress={() => this.setState({ vpaModalSelected: vpa })}
                                        activeOpacity={0.7}
                                    >
                                        <View style={[s.radioOuter, vpa === vpaModalSelected && { borderColor: color }]}>
                                            {vpa === vpaModalSelected && <View style={[s.radioInner, { backgroundColor: color }]} />}
                                        </View>
                                        <Text style={[s.vpaOptionText, vpa === vpaModalSelected && { color, fontWeight: '600' }]}>{vpa}</Text>
                                    </TouchableOpacity>
                                ))
                            )}
                        </ScrollView>

                        <View style={s.vpaModalFooter}>
                            <TouchableOpacity style={s.vpaModalCancelBtn} onPress={this.closeVpaModal}>
                                <Text style={{ color: '#666', fontSize: 15 }}>Cancel</Text>
                            </TouchableOpacity>
                            <TouchableOpacity
                                style={[s.vpaModalConfirmBtn, { backgroundColor: vpaModalSelected ? color : '#ccc' }]}
                                onPress={this.confirmVpa}
                                disabled={!vpaModalSelected}
                            >
                                <Text style={{ color: '#fff', fontSize: 15, fontWeight: '600' }}>Confirm</Text>
                            </TouchableOpacity>
                        </View>
                    </Animatable.View>
                </View>
            </Modal>
        );
    }

    render() {
        const { proxyStatus, proxyError, loadingWallets } = this.state;
        const proxyCfg: Record<string, { label: string; color: string }> = {
            idle: { label: 'Idle', color: '#95a5a6' },
            connecting: { label: 'Connecting…', color: '#f39c12' },
            connected: { label: 'Connected', color: '#2ecc71' },
            disconnected: { label: 'Disconnected', color: '#e74c3c' },
            error: { label: 'Error', color: '#e74c3c' },
        };
        const { label, color } = proxyCfg[proxyStatus];

        return (
            <View style={s.container}>
                {/* top bar */}
                <View style={s.topBar}>
                    <View style={{ flex: 1 }}>
                        <View style={{ flexDirection: 'row', alignItems: 'center' }}>
                            <View style={{ width: 8, height: 8, borderRadius: 4, backgroundColor: color, marginRight: 6 }} />
                            <Text style={{ color, fontSize: 13 }}>
                                Proxy {label}{proxyStatus === 'error' && proxyError ? `: ${proxyError}` : ''}
                            </Text>
                        </View>
                    </View>
                    <View style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>
                        <TouchableOpacity
                            onPress={() => {
                                const domain = getServerDomain();
                                const i = domain.lastIndexOf(':');
                                const host = i > 0 ? domain.slice(0, i) : domain;
                                const port = i > 0 ? domain.slice(i + 1) : '';
                                this.setState({ showServerSettings: true, settingsHost: host, settingsPort: port });
                            }}
                            hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }}
                            style={{ padding: 6 }}
                        >
                            <Text style={{ fontSize: 20, color: '#3498db' }}>⚙</Text>
                        </TouchableOpacity>
                        <TouchableOpacity
                            onPress={() => this.setState({ showAddWallet: true })}
                            style={s.addBtn}
                        >
                            <Text style={s.addBtnText}>+ Add</Text>
                        </TouchableOpacity>
                    </View>
                </View>

                {/* wallet list */}
                <View style={s.listHeader}>
                    <Text style={s.listHeaderText}>Bound Wallets</Text>
                    <TouchableOpacity onPress={this.fetchWallets} disabled={loadingWallets}>
                        <Text style={{ fontSize: 13, color: '#3498db' }}>{loadingWallets ? 'Refreshing…' : 'Refresh'}</Text>
                    </TouchableOpacity>
                </View>

                {this.renderBoundWalletList()}

                {this.renderBindModal()}
                <ServerSettingsModal
                    visible={this.state.showServerSettings}
                    host={this.state.settingsHost}
                    port={this.state.settingsPort}
                    onHostChange={(settingsHost) => this.setState({ settingsHost })}
                    onPortChange={(settingsPort) => this.setState({ settingsPort })}
                    onClose={() => this.setState({ showServerSettings: false })}
                />
                <WalletSelectModal
                    visible={this.state.showAddWallet}
                    onClose={() => this.setState({ showAddWallet: false })}
                    onSelectBind={this.openWalletBind}
                />
                {this.renderVpaModal()}
            </View>
        );
    }
}

const s = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#f0f0f0',
    },
    topBar: {
        flexDirection: 'row',
        alignItems: 'center',
        justifyContent: 'space-between',
        paddingHorizontal: 14,
        paddingVertical: 8,
        backgroundColor: '#fff',
        borderBottomWidth: 1,
        borderBottomColor: '#e0e0e0',
    },
    autoRebindRow: {
        flexDirection: 'row',
        alignItems: 'center',
        marginTop: 6,
        gap: 8,
    },
    autoRebindLabel: {
        fontSize: 12,
        color: '#666',
    },
    addBtn: {
        backgroundColor: '#3498db',
        borderRadius: 6,
        paddingHorizontal: 10,
        paddingVertical: 4,
    },
    addBtnText: {
        color: '#fff',
        fontSize: 13,
        fontWeight: '600',
    },
    listHeader: {
        flexDirection: 'row',
        justifyContent: 'space-between',
        alignItems: 'center',
        paddingHorizontal: 14,
        paddingVertical: 10,
    },
    listHeaderText: {
        fontSize: 14,
        fontWeight: '600',
        color: '#333',
    },
    walletCard: {
        backgroundColor: '#fff',
        borderRadius: 10,
        padding: 14,
        marginBottom: 10,
        elevation: 1,
        shadowColor: '#000',
        shadowOpacity: 0.06,
        shadowRadius: 4,
        shadowOffset: { width: 0, height: 2 },
    },
    walletActions: {
        flexDirection: 'row',
        alignItems: 'center',
        justifyContent: 'flex-end',
        marginTop: 10,
        paddingTop: 10,
        borderTopWidth: StyleSheet.hairlineWidth,
        borderTopColor: '#eee',
    },
    rebindBtn: {
        paddingHorizontal: 12,
        paddingVertical: 6,
        borderRadius: 6,
        backgroundColor: '#3498db',
        marginRight: 12,
    },
    rebindBtnText: {
        color: '#fff',
        fontSize: 13,
        fontWeight: '600',
    },
    walletBadge: {
        width: 52,
        height: 52,
        borderRadius: 10,
        overflow: 'hidden',
        alignItems: 'center',
        justifyContent: 'center',
        backgroundColor: '#f5f5f5',
    },
    walletIcon: {
        width: 52,
        height: 52,
        borderRadius: 10,
    },
    walletIconInactive: { opacity: 0.3 },
    walletBadgeInactive: { backgroundColor: '#f0f0f0' },
    walletIconFallback: {
        width: 52,
        height: 52,
        borderRadius: 10,
        alignItems: 'center',
        justifyContent: 'center',
    },
    walletBadgeText: {
        color: '#fff',
        fontSize: 9,
        fontWeight: '700',
        textAlign: 'center',
    },
    walletPhone: {
        fontSize: 15,
        fontWeight: '600',
        color: '#222',
    },
    walletUpi: {
        fontSize: 12,
        color: '#888',
        marginTop: 2,
    },
    statusDot: {
        width: 8,
        height: 8,
        borderRadius: 4,
    },
    vpaModalBox: {
        backgroundColor: '#fff',
        borderRadius: 20,
        width: '88%',
        maxHeight: '72%',
        overflow: 'hidden',
        shadowColor: '#000',
        shadowOpacity: 0.18,
        shadowRadius: 20,
        shadowOffset: { width: 0, height: 8 },
        elevation: 10,
    },
    vpaModalTitle: {
        fontSize: 17,
        fontWeight: '700',
        color: '#222',
        paddingHorizontal: 20,
        paddingTop: 22,
    },
    vpaModalSub: {
        fontSize: 13,
        color: '#999',
        paddingHorizontal: 20,
        marginTop: 3,
        marginBottom: 14,
    },
    vpaModalList: {
        paddingHorizontal: 14,
        maxHeight: 260,
    },
    vpaOptionRow: {
        flexDirection: 'row',
        alignItems: 'center',
        paddingVertical: 12,
        paddingHorizontal: 12,
        borderRadius: 8,
        borderWidth: 1.5,
        borderColor: '#eee',
        marginBottom: 8,
    },
    radioOuter: {
        width: 20,
        height: 20,
        borderRadius: 10,
        borderWidth: 2,
        borderColor: '#ccc',
        alignItems: 'center',
        justifyContent: 'center',
        marginRight: 12,
    },
    radioInner: {
        width: 10,
        height: 10,
        borderRadius: 5,
    },
    vpaOptionText: {
        fontSize: 14,
        color: '#333',
        flex: 1,
    },
    vpaModalFooter: {
        flexDirection: 'row',
        borderTopWidth: 1,
        borderTopColor: '#f0f0f0',
        marginTop: 8,
    },
    vpaModalCancelBtn: {
        flex: 1,
        paddingVertical: 16,
        alignItems: 'center',
        borderRightWidth: 1,
        borderRightColor: '#f0f0f0',
    },
    vpaModalConfirmBtn: {
        flex: 1,
        paddingVertical: 16,
        alignItems: 'center',
        borderRadius: 0,
    },
    modalOverlay: {
        flex: 1,
        backgroundColor: 'rgba(0,0,0,0.5)',
        justifyContent: 'center',
        alignItems: 'center',
    },
    boundWalletGroup: {
        marginBottom: 4,
    },
    boundWalletGroupHeader: {
        flexDirection: 'row',
        alignItems: 'center',
        backgroundColor: '#fff',
        borderRadius: 10,
        padding: 12,
        marginBottom: 8,
        borderWidth: 1,
        borderColor: '#eee',
    },
    boundWalletGroupTitle: {
        fontSize: 15,
        fontWeight: '600',
        color: '#222',
    },
    boundWalletGroupSub: {
        fontSize: 12,
        color: '#888',
        marginTop: 2,
    },
    boundWalletGroupItem: {
        marginLeft: 10,
    },
    walletGroupChevron: {
        fontSize: 12,
        color: '#999',
        marginLeft: 8,
        width: 16,
        textAlign: 'right',
    },
    otpBar: {
        position: 'absolute',
        bottom: 0,
        left: 0,
        right: 0,
        backgroundColor: '#fff',
        padding: 16,
        borderTopWidth: 1,
        borderTopColor: '#e0e0e0',
        gap: 10,
    },
    otpInput: {
        borderWidth: 1,
        borderColor: '#ccc',
        borderRadius: 8,
        paddingHorizontal: 12,
        paddingVertical: 10,
        fontSize: 15,
        color: '#333',
    },
    otpBtn: {
        backgroundColor: '#5a2d9c',
        borderRadius: 8,
        paddingVertical: 12,
        alignItems: 'center',
    },
    otpBtnText: {
        color: '#fff',
        fontSize: 15,
        fontWeight: '600',
    },
    errText: {
        color: '#e53935',
        fontSize: 13,
    },
});
