<script setup lang="ts">
/**
 * Админка SaaS → Биллинг.
 *
 * Сводный биллинг по всем тенантам: выручка, MRR, retention, refunds.
 * Источник данных: aggregate balance_transactions / invoices / tariff_subscriptions.
 *
 * Данные грузятся с backend GET /api/admin/billing.
 */
import { computed, onMounted, reactive, ref } from 'vue';
import { usePolling } from '../../composables/usePolling';
import * as adminApi from '../../api/admin';
import type { AdminTariffPlan } from '../../api/admin';
import { extractErrorMessage } from '../../api/client';

const search = ref('');

type BillingRow = {
    id: number;
    name: string;
    inn: string; // в API нет — пустая строка
    tariff: string;
    balance_rub: number;
    monthly_topups_rub: number;
    monthly_charges_rub: number;
    mrr_rub: number;
    status: string;
};

const rowsState = reactive<BillingRow[]>([]);

const summary = reactive({
    total_mrr_rub: 0,
    monthly_revenue_rub: 0,
    overdue_count: 0,
    refunds_count_30d: 0,
});

const loading = ref(false);
const fetchError = ref(false);

function deriveStatus(api: { status: string; balance_rub: string; chargeback_unrecovered_rub: string }): string {
    const balance = parseFloat(api.balance_rub);
    const chargeback = parseFloat(api.chargeback_unrecovered_rub);
    if (api.status === 'suspended') return 'suspended';
    if (chargeback > 0 || balance < 0) return 'overdue';
    return 'active';
}

async function loadBilling() {
    loading.value = true;
    fetchError.value = false;
    try {
        const res = await adminApi.listAdminBilling();
        const mapped: BillingRow[] = res.tenants.map((t) => ({
            id: t.id,
            name: t.organization_name,
            inn: '', // нет в API
            tariff: t.tariff_name ?? '—',
            balance_rub: parseFloat(t.balance_rub),
            monthly_topups_rub: parseFloat(t.monthly_topups_rub),
            monthly_charges_rub: parseFloat(t.monthly_charges_rub),
            mrr_rub: parseFloat(t.mrr_rub),
            status: deriveStatus(t),
        }));
        rowsState.splice(0, rowsState.length, ...mapped);
        summary.total_mrr_rub = parseFloat(res.summary.total_mrr_rub);
        summary.monthly_revenue_rub = parseFloat(res.summary.monthly_revenue_rub);
        summary.overdue_count = res.summary.overdue_count;
        summary.refunds_count_30d = res.summary.refunds_count_30d;
    } catch {
        fetchError.value = true;
    } finally {
        loading.value = false;
    }
}

onMounted(loadBilling);
usePolling(loadBilling);

// === Row-actions state (Sprint 3D G4) ===

const actionDialog = ref<null | 'status' | 'refund' | 'tariff'>(null);
const actionRow = ref<BillingRow | null>(null);
const actionReason = ref('');
const actionAmount = ref<number | null>(null);
const actionTariffId = ref<number | null>(null);
const actionLoading = ref(false);
const actionError = ref('');
const tariffPlans = ref<AdminTariffPlan[]>([]);

async function openAction(type: 'status' | 'refund' | 'tariff', row: BillingRow) {
    actionDialog.value = type;
    actionRow.value = row;
    actionReason.value = '';
    actionAmount.value = null;
    actionTariffId.value = null;
    actionError.value = '';

    if (type === 'tariff') {
        try {
            tariffPlans.value = await adminApi.listAdminTariffPlans();
        } catch (e) {
            actionError.value = extractErrorMessage(e);
        }
    }
}

async function confirmAction() {
    // Validate
    if (actionReason.value.trim().length < 10) {
        actionError.value = 'Укажите основание (минимум 10 символов).';
        return;
    }
    if (
        actionDialog.value === 'refund' &&
        (actionAmount.value === null || !Number.isFinite(actionAmount.value) || actionAmount.value <= 0)
    ) {
        actionError.value = 'Укажите сумму возврата больше нуля.';
        return;
    }
    if (actionDialog.value === 'refund' && actionAmount.value! > actionRow.value!.balance_rub) {
        actionError.value = 'Сумма возврата превышает баланс тенанта.';
        return;
    }
    if (actionDialog.value === 'tariff' && actionTariffId.value === null) {
        actionError.value = 'Выберите тарифный план.';
        return;
    }

    const row = actionRow.value!;
    actionLoading.value = true;
    actionError.value = '';

    try {
        if (actionDialog.value === 'status') {
            const newStatus = row.status === 'suspended' ? 'active' : 'suspended';
            await adminApi.updateTenantStatus(row.id, newStatus, actionReason.value.trim());
        } else if (actionDialog.value === 'refund') {
            await adminApi.refundTenant(row.id, actionAmount.value!, actionReason.value.trim());
        } else if (actionDialog.value === 'tariff') {
            await adminApi.changeTenantTariff(row.id, actionTariffId.value!, actionReason.value.trim());
        }
        await loadBilling();
        actionDialog.value = null;
    } catch (e) {
        actionError.value = extractErrorMessage(e);
    } finally {
        actionLoading.value = false;
    }
}

defineExpose({
    rowsState,
    summary,
    loading,
    fetchError,
    loadBilling,
    actionDialog,
    actionRow,
    actionReason,
    actionAmount,
    actionTariffId,
    actionError,
    actionLoading,
    tariffPlans,
    openAction,
    confirmAction,
});

const headers = [
    { title: 'Тенант', key: 'name', sortable: true },
    { title: 'Тариф', key: 'tariff', sortable: true },
    { title: 'Баланс', key: 'balance_rub', sortable: true, align: 'end' as const },
    { title: 'Пополнения за мес', key: 'monthly_topups_rub', sortable: true, align: 'end' as const },
    { title: 'Списания за мес', key: 'monthly_charges_rub', sortable: true, align: 'end' as const },
    { title: 'MRR', key: 'mrr_rub', sortable: true, align: 'end' as const },
    { title: 'Статус', key: 'status', sortable: true },
    { title: '', key: 'actions', sortable: false, align: 'end' as const },
];

const filteredRows = computed(() => {
    const q = search.value.trim().toLowerCase();
    if (!q) return rowsState;
    return rowsState.filter((r) => r.name.toLowerCase().includes(q) || r.inn.includes(q));
});

const fmt = new Intl.NumberFormat('ru-RU');
const formatRub = (v: number) => `${fmt.format(v)} ₽`;

const statusMeta: Record<string, { label: string; color: string }> = {
    active: { label: 'Активен', color: 'success' },
    overdue: { label: 'Просрочка', color: 'warning' },
    suspended: { label: 'Заблокирован', color: 'error' },
};
function statusInfo(s: string) {
    return statusMeta[s] ?? { label: s, color: 'default' };
}

// Mock-tariff slugs → русский title; backend возвращает уже переведённое имя
// (например «Команда»), которое отдаём как есть.
const tariffMap: Record<string, string> = {
    start: 'Старт',
    basic: 'Базовый',
    pro: 'Команда',
    enterprise: 'Enterprise',
};
function tariffLabel(t: string): string {
    return tariffMap[t] ?? t;
}
</script>

<template>
    <v-container fluid class="admin-billing pa-6">
        <header class="page-head mb-4 d-flex justify-space-between align-start">
            <div>
                <h1 class="text-h4 page-title">Биллинг</h1>
                <p class="text-body-2 text-medium-emphasis ma-0">
                    Сводка по всем тенантам: выручка, MRR, просрочки, возвраты.
                </p>
            </div>
            <v-btn
                variant="outlined"
                prepend-icon="mdi-refresh"
                :loading="loading"
                data-testid="reload-btn"
                @click="loadBilling"
            >
                Обновить
            </v-btn>
        </header>

        <v-alert
            v-if="fetchError"
            type="warning"
            variant="tonal"
            density="compact"
            closable
            class="mb-4"
            data-testid="fetch-error-alert"
        >
            Не удалось загрузить биллинг. Попробуйте обновить.
        </v-alert>

        <!-- Stats row -->
        <v-row class="mb-4" data-testid="billing-stats">
            <v-col cols="12" sm="6" md="3">
                <v-card variant="outlined" class="pa-3">
                    <div class="text-caption text-medium-emphasis">MRR</div>
                    <div class="text-h6 font-mono tabular">{{ formatRub(summary.total_mrr_rub) }}</div>
                </v-card>
            </v-col>
            <v-col cols="12" sm="6" md="3">
                <v-card variant="outlined" class="pa-3">
                    <div class="text-caption text-medium-emphasis">Выручка за месяц</div>
                    <div class="text-h6 font-mono tabular">
                        {{ formatRub(summary.monthly_revenue_rub) }}
                    </div>
                </v-card>
            </v-col>
            <v-col cols="12" sm="6" md="3">
                <v-card variant="outlined" class="pa-3">
                    <div class="text-caption text-medium-emphasis">Просрочка</div>
                    <div class="text-h6 font-mono tabular text-warning">
                        {{ summary.overdue_count }}
                    </div>
                </v-card>
            </v-col>
            <v-col cols="12" sm="6" md="3">
                <v-card variant="outlined" class="pa-3">
                    <div class="text-caption text-medium-emphasis">Возвраты за 30 дн</div>
                    <div class="text-h6 font-mono tabular">{{ summary.refunds_count_30d }}</div>
                </v-card>
            </v-col>
        </v-row>

        <v-card variant="outlined" class="pa-4">
            <div class="d-flex justify-space-between align-center mb-3">
                <h2 class="text-h6 ma-0">Тенанты</h2>
                <v-text-field
                    v-model="search"
                    label="Поиск"
                    placeholder="по названию или ИНН"
                    prepend-inner-icon="mdi-magnify"
                    density="compact"
                    variant="outlined"
                    hide-details
                    clearable
                    style="max-width: 320px"
                />
            </div>

            <v-data-table
                :headers="headers"
                :items="filteredRows"
                :items-per-page="20"
                density="comfortable"
                class="font-mono-nums"
            >
                <template #[`item.name`]="{ item }">
                    <div>
                        <div class="font-weight-medium">{{ item.name }}</div>
                        <div v-if="item.inn" class="text-caption text-medium-emphasis">ИНН {{ item.inn }}</div>
                    </div>
                </template>
                <template #[`item.tariff`]="{ item }">
                    {{ tariffLabel(item.tariff) }}
                </template>
                <template #[`item.balance_rub`]="{ item }">
                    <span
                        :class="
                            item.balance_rub < 0 ? 'text-error' : item.balance_rub === 0 ? 'text-medium-emphasis' : ''
                        "
                    >
                        {{ formatRub(item.balance_rub) }}
                    </span>
                </template>
                <template #[`item.monthly_topups_rub`]="{ item }">
                    {{ formatRub(item.monthly_topups_rub) }}
                </template>
                <template #[`item.monthly_charges_rub`]="{ item }">
                    {{ formatRub(item.monthly_charges_rub) }}
                </template>
                <template #[`item.mrr_rub`]="{ item }">
                    {{ formatRub(item.mrr_rub) }}
                </template>
                <template #[`item.status`]="{ item }">
                    <v-chip :color="statusInfo(item.status).color" size="small" variant="tonal">
                        {{ statusInfo(item.status).label }}
                    </v-chip>
                </template>
                <template #[`item.actions`]="{ item }">
                    <v-menu>
                        <template #activator="{ props }">
                            <v-btn
                                v-bind="props"
                                icon="mdi-dots-vertical"
                                :data-testid="`row-actions-${item.id}`"
                                variant="text"
                                size="small"
                                aria-label="Действия с тенантом"
                            />
                        </template>
                        <v-list density="compact">
                            <v-list-item
                                :title="item.status === 'suspended' ? 'Разблокировать' : 'Приостановить'"
                                prepend-icon="mdi-account-cancel"
                                @click="openAction('status', item)"
                            />
                            <v-list-item
                                title="Возврат средств"
                                prepend-icon="mdi-cash-refund"
                                @click="openAction('refund', item)"
                            />
                            <v-list-item
                                title="Сменить тариф"
                                prepend-icon="mdi-swap-horizontal"
                                @click="openAction('tariff', item)"
                            />
                        </v-list>
                    </v-menu>
                </template>
            </v-data-table>
        </v-card>

        <!-- Dialog: suspend / activate -->
        <v-dialog :model-value="actionDialog === 'status'" max-width="480" @update:model-value="actionDialog = null">
            <v-card>
                <v-card-title>
                    {{ actionRow?.status === 'suspended' ? 'Разблокировать тенанта' : 'Приостановить тенанта' }}
                </v-card-title>
                <v-card-text>
                    <p class="mb-3 text-body-2">
                        Тенант: <strong>{{ actionRow?.name }}</strong>
                    </p>
                    <v-alert
                        v-if="actionError"
                        type="error"
                        variant="tonal"
                        density="compact"
                        class="mb-3"
                        data-testid="action-error"
                    >
                        {{ actionError }}
                    </v-alert>
                    <v-textarea
                        v-model="actionReason"
                        label="Основание"
                        placeholder="Минимум 10 символов"
                        rows="3"
                        variant="outlined"
                        density="compact"
                        data-testid="action-reason"
                    />
                </v-card-text>
                <v-card-actions class="justify-end">
                    <v-btn variant="text" @click="actionDialog = null">Отмена</v-btn>
                    <v-btn :loading="actionLoading" color="primary" variant="flat" @click="confirmAction">
                        Подтвердить
                    </v-btn>
                </v-card-actions>
            </v-card>
        </v-dialog>

        <!-- Dialog: refund -->
        <v-dialog :model-value="actionDialog === 'refund'" max-width="480" @update:model-value="actionDialog = null">
            <v-card>
                <v-card-title>Возврат средств</v-card-title>
                <v-card-text>
                    <p class="mb-3 text-body-2">
                        Тенант: <strong>{{ actionRow?.name }}</strong>
                    </p>
                    <v-alert
                        v-if="actionError"
                        type="error"
                        variant="tonal"
                        density="compact"
                        class="mb-3"
                        data-testid="action-error"
                    >
                        {{ actionError }}
                    </v-alert>
                    <v-text-field
                        v-model.number="actionAmount"
                        type="number"
                        label="Сумма возврата, ₽"
                        :hint="actionRow ? `доступно к возврату: ${formatRub(actionRow.balance_rub)}` : ''"
                        persistent-hint
                        variant="outlined"
                        density="compact"
                        class="mb-3"
                        data-testid="refund-amount"
                    />
                    <v-textarea
                        v-model="actionReason"
                        label="Основание"
                        placeholder="Минимум 10 символов"
                        rows="3"
                        variant="outlined"
                        density="compact"
                        data-testid="action-reason"
                    />
                </v-card-text>
                <v-card-actions class="justify-end">
                    <v-btn variant="text" @click="actionDialog = null">Отмена</v-btn>
                    <v-btn :loading="actionLoading" color="primary" variant="flat" @click="confirmAction">
                        Выполнить возврат
                    </v-btn>
                </v-card-actions>
            </v-card>
        </v-dialog>

        <!-- Dialog: change tariff -->
        <v-dialog :model-value="actionDialog === 'tariff'" max-width="480" @update:model-value="actionDialog = null">
            <v-card>
                <v-card-title>Сменить тариф</v-card-title>
                <v-card-text>
                    <p class="mb-3 text-body-2">
                        Тенант: <strong>{{ actionRow?.name }}</strong>
                    </p>
                    <v-alert
                        v-if="actionError"
                        type="error"
                        variant="tonal"
                        density="compact"
                        class="mb-3"
                        data-testid="action-error"
                    >
                        {{ actionError }}
                    </v-alert>
                    <v-select
                        v-model="actionTariffId"
                        :items="tariffPlans"
                        item-title="name"
                        item-value="id"
                        label="Тарифный план"
                        variant="outlined"
                        density="compact"
                        class="mb-3"
                        data-testid="tariff-select"
                    />
                    <v-textarea
                        v-model="actionReason"
                        label="Основание"
                        placeholder="Минимум 10 символов"
                        rows="3"
                        variant="outlined"
                        density="compact"
                        data-testid="action-reason"
                    />
                </v-card-text>
                <v-card-actions class="justify-end">
                    <v-btn variant="text" @click="actionDialog = null">Отмена</v-btn>
                    <v-btn :loading="actionLoading" color="primary" variant="flat" @click="confirmAction">
                        Сменить тариф
                    </v-btn>
                </v-card-actions>
            </v-card>
        </v-dialog>
    </v-container>
</template>

<style scoped>
.admin-billing {
    max-width: 1400px;
}
.page-title {
    font-variation-settings: 'opsz' 28;
    letter-spacing: -0.018em;
}
.font-mono {
    font-family: 'JetBrains Mono', ui-monospace, monospace;
}
.tabular {
    font-feature-settings: 'tnum';
}
</style>
