<script setup lang="ts">
/**
 * Отчёты — асинхронная генерация выгрузок (CSV/XLSX/JSON/PDF) с очередью
 * и квотой 3 одновременных (CTO-7) + retry до 3 попыток в окне 7 дней (CTO-6).
 *
 * Backend (этапы 1-3 эпика): `App\Http\Controllers\Api\ReportJobController`.
 * Этап 4: интеграция API в этом view'е.
 *
 * Polling 30 сек обновляет список (через usePolling).
 */
import { computed, onMounted, ref } from 'vue';
import { cancelReportJob, createReportJob, deleteReportJob, listReportJobs, retryReportJob } from '../api/reports';
import { extractErrorMessage, extractValidationErrors } from '../api/client';
import { usePolling } from '../composables/usePolling';
import { POLLING_INTERVAL_MS } from '../constants/polling';
import { type ReportFormat, type ReportJob, type ReportType } from '../composables/mockReports';
import { mapApiReportJob, uiTypeToApi } from '../composables/reportsMapper';
import ReportRequestForm from '../components/reports/ReportRequestForm.vue';
import ReportJobsList from '../components/reports/ReportJobsList.vue';

const selectedType = ref<ReportType>('deals');
const dateFrom = ref(new Date().toISOString().slice(0, 10));
const dateTo = ref(new Date().toISOString().slice(0, 10));
const project = ref('Все проекты');
const manager = ref('Все');
const selectedFormat = ref<ReportFormat>('csv');

// Фейк-варианты убраны (захардкожены, не из API и даже не отправлялись на бэк).
// Пока только «Все» — реальные проекты/менеджеры будут подведены из API.
const projectOptions = ['Все проекты'];
const managerOptions = ['Все'];

// State
const jobs = ref<ReportJob[]>([]);
const loading = ref(false);
const submitting = ref(false);
const fetchError = ref<string | null>(null);
const submitError = ref<string | null>(null);
const submitSuccess = ref(false);
const quotaActive = ref(0);
const quotaMax = ref(3);
const totalDone = ref(0);

async function loadJobs(): Promise<void> {
    loading.value = true;
    try {
        const data = await listReportJobs({ limit: 50 });
        const now = new Date();
        jobs.value = data.jobs.map((j) => mapApiReportJob(j, now));
        quotaActive.value = data.quota.active;
        quotaMax.value = data.quota.max_active;
        totalDone.value = data.counts.done;
        fetchError.value = null;
    } catch (e) {
        fetchError.value = extractErrorMessage(e);
    } finally {
        loading.value = false;
    }
}

onMounted(() => {
    void loadJobs();
});

usePolling(loadJobs, { intervalMs: POLLING_INTERVAL_MS });

async function submitForm(): Promise<void> {
    submitting.value = true;
    submitError.value = null;
    submitSuccess.value = false;
    try {
        await createReportJob({
            type: uiTypeToApi(selectedType.value),
            format: selectedFormat.value,
            parameters: {
                date_from: dateFrom.value,
                date_to: dateTo.value,
            },
        });
        submitSuccess.value = true;
        await loadJobs();
    } catch (e) {
        const validation = extractValidationErrors(e);
        if (validation && Object.keys(validation).length > 0) {
            submitError.value = Object.values(validation).flat().join(' ');
        } else {
            submitError.value = extractErrorMessage(e);
        }
    } finally {
        submitting.value = false;
    }
}

function resetForm(): void {
    selectedType.value = 'deals';
    selectedFormat.value = 'csv';
    project.value = 'Все проекты';
    manager.value = 'Все';
    dateFrom.value = new Date().toISOString().slice(0, 10);
    dateTo.value = new Date().toISOString().slice(0, 10);
    submitError.value = null;
    submitSuccess.value = false;
}

async function onRetry(jobId: number): Promise<void> {
    try {
        await retryReportJob(jobId);
        await loadJobs();
    } catch (e) {
        const validation = extractValidationErrors(e);
        if (validation && Object.keys(validation).length > 0) {
            submitError.value = Object.values(validation).flat().join(' ');
        } else {
            submitError.value = extractErrorMessage(e);
        }
    }
}

async function onCancel(jobId: number): Promise<void> {
    // Действие над существующей задачей снимает зелёный «отчёт создан» —
    // иначе success-баннер висит после отмены (F-минор ui-audit-round2).
    submitSuccess.value = false;
    try {
        await cancelReportJob(jobId);
        await loadJobs();
    } catch (e) {
        submitError.value = extractErrorMessage(e);
    }
}

const deleteDialog = ref(false);
const deleteTargetId = ref<number | null>(null);

function askDelete(jobId: number): void {
    deleteTargetId.value = jobId;
    deleteDialog.value = true;
}

async function confirmDelete(): Promise<void> {
    if (deleteTargetId.value === null) return;
    try {
        await deleteReportJob(deleteTargetId.value);
        await loadJobs();
    } catch (e) {
        submitError.value = extractErrorMessage(e);
    } finally {
        deleteDialog.value = false;
        deleteTargetId.value = null;
    }
}

const canSubmit = computed(() => quotaActive.value < quotaMax.value && !submitting.value);
</script>

<template>
    <v-container fluid class="reports pa-6">
        <header class="page-head">
            <div>
                <h1 class="text-h4 mb-2 page-title">Отчёты</h1>
                <div class="page-stats text-body-2 text-medium-emphasis">
                    очередь
                    <span class="num text-primary">{{ quotaActive }}</span
                    >/<span class="num">{{ quotaMax }}</span>
                    <span class="sep">·</span>
                    готово <span class="num">{{ totalDone }}</span>
                </div>
            </div>
            <v-btn
                variant="outlined"
                size="small"
                prepend-icon="mdi-refresh"
                :loading="loading"
                data-testid="reload-btn"
                @click="loadJobs"
            >
                Обновить
            </v-btn>
        </header>

        <v-alert
            v-if="fetchError"
            type="warning"
            variant="tonal"
            density="compact"
            class="mt-3"
            closable
            data-testid="fetch-error-alert"
        >
            Backend недоступен: {{ fetchError }}
        </v-alert>

        <ReportRequestForm
            v-model:selected-type="selectedType"
            v-model:date-from="dateFrom"
            v-model:date-to="dateTo"
            v-model:project="project"
            v-model:manager="manager"
            v-model:selected-format="selectedFormat"
            :project-options="projectOptions"
            :manager-options="managerOptions"
            :submitting="submitting"
            :submit-success="submitSuccess"
            :submit-error="submitError"
            :can-submit="canSubmit"
            :quota-active="quotaActive"
            :quota-max="quotaMax"
            @submit="submitForm"
            @reset="resetForm"
        />

        <ReportJobsList :jobs="jobs" @retry="onRetry" @cancel="onCancel" @request-delete="askDelete" />

        <v-dialog v-model="deleteDialog" max-width="420" persistent>
            <v-card>
                <v-card-title>Удалить отчёт?</v-card-title>
                <v-card-text>Файл будет удалён вместе с записью. Действие необратимо.</v-card-text>
                <v-card-actions>
                    <v-spacer />
                    <v-btn variant="text" @click="deleteDialog = false">Отмена</v-btn>
                    <v-btn color="error" variant="flat" data-testid="delete-confirm-btn" @click="confirmDelete"
                        >Удалить</v-btn
                    >
                </v-card-actions>
            </v-card>
        </v-dialog>
    </v-container>
</template>

<style scoped>
.reports {
    max-width: 1440px;
}

.page-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    flex-wrap: wrap;
    gap: 12px;
}

.page-title {
    font-variation-settings: 'opsz' 28;
    letter-spacing: -0.018em;
}

.page-stats {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
}
.page-stats .sep {
    /* WCAG2AA 4.5:1 fix (was #92907b → 2.92:1 on ivory; #6b6356 → 5.33:1). */
    color: #6b6356;
}

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