<script setup lang="ts">
/**
 * Диалог impersonation flow в AdminTenantsView (ТЗ §22.7 / Ю-1).
 *
 * 3-step state-machine:
 *   1. 'reason'  — textarea для основания (≥30 chars) → POST /api/admin/impersonation/init.
 *   2. 'verify'  — показ email клиента + ввод 6-значного кода → /api/admin/impersonation/verify.
 *                  На dev показывается _dev_plain_code (на prod исчезнет после MailService).
 *   3. 'active'  — chip «Сессия активна», кнопка «Завершить» → /api/admin/impersonation/end.
 *
 * NB: на MVP saas-admin auth не реализован, requested_by передаётся параметром
 * (стандартный admin user id = 1; на prod заменится request()->user()->id).
 *
 * Two-person approval (CTO-15/Ю-9) для тенантов с processing_restricted/
 * chargeback_unrecovered_rub > 0 — TODO после saas-admin auth.
 */
import * as adminApi from '../../api/admin';
import { extractErrorMessage, extractValidationErrors } from '../../api/client';
import type { AdminTenant } from '../../composables/mockTenants';
import { computed, ref, watch } from 'vue';

// Vue 3.5 defineModel() — Sprint 2 Phase B / O-stack-04.
const dialogOpen = defineModel<boolean>({ required: true });

const props = defineProps<{
    tenant: AdminTenant | null;
    /**
     * ID admin-пользователя, инициирующего impersonation. На MVP передаётся
     * родителем (заглушка — id из mock-данных). Production: backend будет
     * брать из auth()->user()->id, frontend перестанет это передавать.
     */
    requestedBy: number;
}>();

type Step = 'reason' | 'verify' | 'active' | 'done';

const step = ref<Step>('reason');
const busy = ref(false);
const errorMessage = ref<string | null>(null);
const reasonError = ref<string | null>(null);
const codeError = ref<string | null>(null);

const reason = ref('');
const code = ref('');

const tokenId = ref<number | null>(null);
const sentToEmail = ref<string | null>(null);
const expiresAt = ref<string | null>(null);
const devPlainCode = ref<string | null>(null);
const usedAtIso = ref<string | null>(null);

const reasonLength = computed(() => reason.value.trim().length);
const reasonRemaining = computed(() => Math.max(0, 30 - reasonLength.value));
const reasonValid = computed(() => reasonLength.value >= 30);

function resetState() {
    step.value = 'reason';
    busy.value = false;
    errorMessage.value = null;
    reasonError.value = null;
    codeError.value = null;
    reason.value = '';
    code.value = '';
    tokenId.value = null;
    sentToEmail.value = null;
    expiresAt.value = null;
    devPlainCode.value = null;
    usedAtIso.value = null;
}

watch(dialogOpen, (open) => {
    if (open) resetState();
});

async function submitInit() {
    if (!props.tenant) return;
    if (!reasonValid.value) {
        reasonError.value = 'Минимум 30 символов.';
        return;
    }
    busy.value = true;
    errorMessage.value = null;
    reasonError.value = null;
    try {
        const r = await adminApi.impersonationInit({
            tenant_id: props.tenant.id,
            requested_by: props.requestedBy,
            reason: reason.value.trim(),
        });
        tokenId.value = r.token_id;
        sentToEmail.value = r.sent_to_email;
        expiresAt.value = r.expires_at;
        devPlainCode.value = r._dev_plain_code ?? null;
        step.value = 'verify';
    } catch (err) {
        const validation = extractValidationErrors(err);
        if (validation?.reason?.[0]) {
            reasonError.value = validation.reason[0];
        } else {
            errorMessage.value = extractErrorMessage(err, 'Не удалось инициировать impersonation.');
        }
    } finally {
        busy.value = false;
    }
}

async function submitVerify() {
    if (tokenId.value === null) return;
    if (!/^\d{6}$/.test(code.value)) {
        codeError.value = 'Введите 6 цифр.';
        return;
    }
    busy.value = true;
    errorMessage.value = null;
    codeError.value = null;
    try {
        const r = await adminApi.impersonationVerify({
            token_id: tokenId.value,
            code: code.value,
        });
        usedAtIso.value = r.used_at;
        step.value = 'active';
    } catch (err) {
        codeError.value = extractErrorMessage(err, 'Неверный код.');
    } finally {
        busy.value = false;
    }
}

async function submitEnd() {
    if (tokenId.value === null) return;
    busy.value = true;
    errorMessage.value = null;
    try {
        await adminApi.impersonationEnd(tokenId.value);
        step.value = 'done';
    } catch (err) {
        errorMessage.value = extractErrorMessage(err, 'Не удалось завершить сессию.');
    } finally {
        busy.value = false;
    }
}

function close() {
    dialogOpen.value = false;
}
</script>

<template>
    <v-dialog v-model="dialogOpen" :max-width="520" persistent data-testid="impersonation-dialog">
        <v-card v-if="tenant">
            <v-card-title class="d-flex align-center">
                <v-icon class="me-2" color="warning">mdi-account-switch</v-icon>
                Войти как клиент
            </v-card-title>

            <v-card-subtitle class="pb-2">
                <strong>{{ tenant.name }}</strong>
                <span class="text-medium-emphasis"> · {{ tenant.code }} · ИНН {{ tenant.inn }}</span>
            </v-card-subtitle>

            <v-card-text>
                <v-alert
                    v-if="errorMessage"
                    type="error"
                    variant="tonal"
                    density="compact"
                    class="mb-3"
                    data-testid="error-alert"
                >
                    {{ errorMessage }}
                </v-alert>

                <!-- STEP 1: REASON -->
                <template v-if="step === 'reason'">
                    <v-alert type="warning" variant="tonal" density="compact" class="mb-3">
                        Действие протоколируется в <code>saas_admin_audit_log</code> и уведомление уходит клиенту по
                        email. Все ваши действия в этой сессии прозрачны для клиента.
                    </v-alert>
                    <p class="text-body-2 mb-2">
                        Опишите основание (тикет поддержки, инцидент, согласованный запрос клиента). Минимум
                        <strong>30 символов</strong> по политике §22.7.
                    </p>
                    <v-textarea
                        v-model="reason"
                        variant="outlined"
                        rows="3"
                        autofocus
                        :counter="200"
                        :error-messages="reasonError ? [reasonError] : []"
                        :hint="reasonValid ? 'Готово к отправке.' : `Ещё ${reasonRemaining} символов`"
                        persistent-hint
                        placeholder="Тикет SUP-12453: клиент сообщил, что в карточке сделки #4471 не сохраняется комментарий…"
                        data-testid="reason-input"
                    />
                </template>

                <!-- STEP 2: VERIFY -->
                <template v-else-if="step === 'verify'">
                    <v-alert type="info" variant="tonal" density="compact" class="mb-3">
                        Код отправлен на email клиента: <strong>{{ sentToEmail }}</strong>
                    </v-alert>
                    <p class="text-body-2 mb-2">
                        Получите 6-значный код у клиента и введите его. Код действует
                        <strong>15 минут</strong>. После 5 неверных попыток токен будет аннулирован.
                    </p>
                    <v-text-field
                        v-model="code"
                        label="6-значный код"
                        inputmode="numeric"
                        maxlength="6"
                        autofocus
                        autocomplete="one-time-code"
                        :error-messages="codeError ? [codeError] : []"
                        density="comfortable"
                        data-testid="code-input"
                    />
                    <v-alert
                        v-if="devPlainCode"
                        type="success"
                        variant="tonal"
                        density="compact"
                        class="mt-3"
                        data-testid="dev-code-banner"
                    >
                        <strong>DEV-only:</strong> код = <code class="font-mono">{{ devPlainCode }}</code> (на prod
                        исчезнет — будет приходить только на email клиента).
                    </v-alert>
                </template>

                <!-- STEP 3: ACTIVE -->
                <template v-else-if="step === 'active'">
                    <v-alert type="success" variant="tonal" density="compact" class="mb-3">
                        Impersonation активен. На production здесь начнётся реальная сессия в кабинете клиента.
                    </v-alert>
                    <p class="text-body-2">
                        Сессия привязана к токену <code class="font-mono">#{{ tokenId }}</code> и автоматически истечёт
                        через 1 час. Завершите её сразу после выполнения задачи — это требование §22.7.
                    </p>
                    <p class="text-caption text-medium-emphasis mt-2">
                        Активирована в {{ new Date(usedAtIso ?? '').toLocaleString('ru-RU') }}.
                    </p>
                </template>

                <!-- STEP 4: DONE -->
                <template v-else-if="step === 'done'">
                    <v-alert type="success" variant="tonal" density="compact">
                        Сессия impersonation завершена. Запись доступна в журнале аудита.
                    </v-alert>
                </template>
            </v-card-text>

            <v-card-actions>
                <v-spacer />
                <template v-if="step === 'reason'">
                    <v-btn variant="text" :disabled="busy" data-testid="cancel-btn" @click="close">Отмена</v-btn>
                    <v-btn
                        color="primary"
                        :loading="busy"
                        :disabled="!reasonValid"
                        data-testid="submit-init-btn"
                        @click="submitInit"
                    >
                        Запросить код
                    </v-btn>
                </template>
                <template v-else-if="step === 'verify'">
                    <v-btn variant="text" :disabled="busy" data-testid="cancel-btn" @click="close">Отмена</v-btn>
                    <v-btn color="primary" :loading="busy" data-testid="submit-verify-btn" @click="submitVerify">
                        Подтвердить
                    </v-btn>
                </template>
                <template v-else-if="step === 'active'">
                    <v-btn color="error" :loading="busy" data-testid="submit-end-btn" @click="submitEnd">
                        Завершить сессию
                    </v-btn>
                </template>
                <template v-else>
                    <v-btn color="primary" data-testid="close-btn" @click="close">Закрыть</v-btn>
                </template>
            </v-card-actions>
        </v-card>
    </v-dialog>
</template>

<style scoped>
.font-mono {
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-feature-settings: 'tnum';
}
</style>
