<template>
    <div v-loading="loading" class="detail-loading-host" element-loading-text="Loading wallet…">
        <el-empty v-if="!loading && loadError" description="Failed to load wallet status" />

        <template v-if="!loading && !loadError && walletStatus">
            <div v-if="walletStatus.syncStatus !== MoneroWalletSyncStatus.Synced" class="mb-16">
                <el-alert type="warning" :closable="false" show-icon title="Wallet is syncing. Please wait." />
            </div>

            <el-card class="mb-24" shadow="never">
                <template #header>
                    <div class="flex items-center justify-between gap-16">
                        <span>Status</span>
                        <el-button type="default" :loading="refreshing" @click="refreshStatus">Refresh</el-button>
                    </div>
                </template>

                <el-descriptions :column="1" border>
                    <el-descriptions-item label="Network">{{ walletStatus.network }}</el-descriptions-item>
                    <el-descriptions-item label="RPC version">{{ walletStatus.rpcVersion }}</el-descriptions-item>
                    <el-descriptions-item label="Wallet height">{{ walletStatus.walletHeight }}</el-descriptions-item>
                    <el-descriptions-item label="Daemon height">
                        {{ walletStatus.daemonHeight ?? 'Unavailable' }}
                    </el-descriptions-item>
                    <el-descriptions-item label="Sync">
                        <el-tag :type="resolveMoneroWalletSyncStatusTagType(walletStatus.syncStatus)" size="small">
                            {{ resolveMoneroWalletSyncStatusLabel(walletStatus.syncStatus) }}
                        </el-tag>
                    </el-descriptions-item>
                    <el-descriptions-item label="Total balance">{{ walletStatus.balanceXmr }} XMR</el-descriptions-item>
                    <el-descriptions-item label="Unlocked balance">
                        {{ walletStatus.unlockedBalanceXmr }} XMR
                    </el-descriptions-item>
                </el-descriptions>
            </el-card>

            <el-card class="mb-24" shadow="never">
                <template #header>
                    <span>Withdraw all</span>
                </template>

                <el-form
                    ref="withdrawFormRef"
                    label-position="top"
                    :model="withdrawForm"
                    :rules="withdrawFormRules"
                    @submit.prevent="onWithdrawSubmit"
                >
                    <el-form-item label="Destination address" prop="destinationAddress">
                        <el-input
                            v-model="withdrawForm.destinationAddress"
                            autocomplete="off"
                            :placeholder="`${walletStatus.network} Monero address`"
                            :disabled="withdrawing"
                            @input="withdrawFormRef?.clearValidate('destinationAddress')"
                        />
                    </el-form-item>

                    <el-button type="primary" native-type="submit" :loading="withdrawing">
                        Withdraw all unlocked funds
                    </el-button>
                </el-form>
            </el-card>

            <el-card shadow="never">
                <template #header>
                    <span>Recovery seed</span>
                </template>

                <p class="m-0 mb-16 secondary-text">
                    This shop is non-custodial. You control the wallet seed. Anyone with the seed can spend all funds.
                    Store it offline and never share it.
                </p>

                <el-button type="danger" plain :loading="revealingSeed" @click="onRevealSeedClick">
                    Reveal seed
                </el-button>
            </el-card>
        </template>

        <el-dialog v-model="seedDialogVisible" title="Recovery seed" width="560px" destroy-on-close @closed="clearSeed">
            <el-alert type="error" :closable="false" show-icon title="Store this offline. Do not share it." />

            <el-input v-model="revealedMnemonic" class="mt-16" type="textarea" :rows="4" readonly autocomplete="off" />
        </el-dialog>
    </div>
</template>

<script setup lang="ts">
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus';
import { computed, onBeforeMount, reactive, ref } from 'vue';
import { storeToRefs } from 'pinia';
import { MoneroWalletSyncStatus } from '@/types/moneroWallet/MoneroWalletSyncStatus';
import { useMoneroWalletStore } from '@/stores/moneroWallet';
import { isMoneroStandardAddress } from '@/utils/monero/isMoneroStandardAddress';
import { resolveAxiosErrorMessage } from '@/utils/resolveAxiosErrorMessage';

const moneroWalletStore = useMoneroWalletStore();

const { status: walletStatus } = storeToRefs(moneroWalletStore);

const { fetchStatus, withdrawAll, revealSeed } = moneroWalletStore;

const loading = ref(true);
const loadError = ref(false);
const refreshing = ref(false);
const withdrawing = ref(false);
const revealingSeed = ref(false);
const withdrawFormRef = ref<FormInstance>();
const withdrawForm = reactive({
    destinationAddress: ''
});
const seedDialogVisible = ref(false);
const revealedMnemonic = ref('');

onBeforeMount(async () => {
    loading.value = true;

    await loadWalletStatus();

    loading.value = false;
});

const withdrawFormRules = computed<FormRules>(() => ({
    destinationAddress: [
        {
            validator: (_rule, value, callback) => {
                if (walletStatus.value?.syncStatus !== MoneroWalletSyncStatus.Synced) {
                    callback(new Error('Wait until the wallet finishes syncing before withdrawing'));

                    return;
                }

                if (typeof value !== 'string' || !value.trim()) {
                    callback(new Error('Enter a destination address'));

                    return;
                }

                const network = walletStatus.value?.network;

                if (!network || !isMoneroStandardAddress(value, network)) {
                    callback(new Error(`Enter a valid ${network ?? 'Monero'} address.`));

                    return;
                }

                callback();
            },
            trigger: ['blur', 'change']
        }
    ]
}));

const loadWalletStatus = async (): Promise<void> => {
    loadError.value = false;

    try {
        await fetchStatus();
    } catch (error) {
        loadError.value = true;

        ElMessage.error(resolveAxiosErrorMessage(error, 'Failed to load wallet status'));
    }
};

const refreshStatus = async (): Promise<void> => {
    refreshing.value = true;

    try {
        await loadWalletStatus();

        ElMessage.success('Wallet status refreshed');
    } finally {
        refreshing.value = false;
    }
};

const promptForPassword = async (title: string): Promise<string | null> => {
    try {
        const { value } = await ElMessageBox.prompt('Enter your CMS password to continue.', title, {
            confirmButtonText: 'Continue',
            cancelButtonText: 'Cancel',
            inputType: 'password',
            inputValidator: value => (value.trim().length > 0 ? true : 'Password is required')
        });

        return value.trim();
    } catch {
        return null;
    }
};

const onWithdrawSubmit = async (): Promise<void> => {
    const formEl = withdrawFormRef.value;

    if (!formEl) {
        return;
    }

    try {
        await formEl.validate();
    } catch {
        return;
    }

    const trimmedAddress = withdrawForm.destinationAddress.trim();

    try {
        await ElMessageBox.confirm(`Withdraw all unlocked funds to:\n${trimmedAddress}`, 'Confirm withdrawal', {
            confirmButtonText: 'Continue',
            cancelButtonText: 'Cancel',
            type: 'warning'
        });
    } catch {
        return;
    }

    const password = await promptForPassword('Confirm withdrawal');

    if (!password) {
        return;
    }

    withdrawing.value = true;

    try {
        const result = await withdrawAll({
            destinationAddress: trimmedAddress,
            password
        });

        ElMessage.success(`Withdrew ${result.amountXmr} XMR`);

        if (result.txHashes.length > 0) {
            await ElMessageBox.alert(result.txHashes.join('\n'), 'Transaction hash(es)', {
                confirmButtonText: 'OK'
            });
        }

        withdrawForm.destinationAddress = '';
        withdrawFormRef.value?.clearValidate();

        await loadWalletStatus();
    } catch (error) {
        ElMessage.error({ message: resolveAxiosErrorMessage(error, 'Withdrawal failed'), duration: 5000 });
    } finally {
        withdrawing.value = false;
    }
};

const onRevealSeedClick = async (): Promise<void> => {
    try {
        await ElMessageBox.confirm(
            'The recovery seed grants full control over this shop wallet. Store it offline. Never share it or enter it on untrusted sites.',
            'Reveal recovery seed?',
            {
                confirmButtonText: 'I understand',
                cancelButtonText: 'Cancel',
                type: 'warning'
            }
        );
    } catch {
        return;
    }

    const password = await promptForPassword('Reveal recovery seed');

    if (!password) {
        return;
    }

    revealingSeed.value = true;

    try {
        const result = await revealSeed({ password });

        revealedMnemonic.value = result.mnemonic;
        seedDialogVisible.value = true;
    } catch (error) {
        ElMessage.error(resolveAxiosErrorMessage(error, 'Could not reveal seed'));
    } finally {
        revealingSeed.value = false;
    }
};

const clearSeed = (): void => {
    revealedMnemonic.value = '';
};

const resolveMoneroWalletSyncStatusLabel = (syncStatus: MoneroWalletSyncStatus): string => {
    switch (syncStatus) {
        case MoneroWalletSyncStatus.Synced:
            return 'Synced';
        case MoneroWalletSyncStatus.Syncing:
            return 'Syncing';
        default:
            return 'Unknown';
    }
};

const resolveMoneroWalletSyncStatusTagType = (syncStatus: MoneroWalletSyncStatus): 'success' | 'warning' | 'info' => {
    switch (syncStatus) {
        case MoneroWalletSyncStatus.Synced:
            return 'success';
        case MoneroWalletSyncStatus.Syncing:
            return 'warning';
        default:
            return 'info';
    }
};
</script>
