import { describe, it, expect, vi, beforeEach } from 'vitest';
import { mount, flushPromises } from '@vue/test-utils';
import { createVuetify } from 'vuetify';
import { createRouter, createMemoryHistory } from 'vue-router';
import AdminDashboardView from '../../resources/js/views/admin/AdminDashboardView.vue';

// Мокаем клиент дашборда: 3 GET-эндпоинта возвращают фикстуры.
vi.mock('../../resources/js/api/adminDashboard', () => ({
    getDashboardSummary: vi.fn().mockResolvedValue({
        period: '7d',
        finance: {
            topups_rub: '320000',
            charges_rub: '180000',
            active_clients: 5,
            new_clients: 2,
            negative_balance_count: 1,
            light: 'red',
        },
        health: {
            light: 'green',
            open_incidents: 0,
            job_errors_24h: 0,
            failed_jobs_24h: 0,
            last_sync_status: 'success',
            last_sync_at: null,
        },
        leads: { light: 'green', delivered_today: 71, received_today: 80, stuck: 0, unrouted: 0 },
        supply: { light: 'red', demand: 250, formula: 160, ordered: 175, mismatches: 1, total_orders: 405, total_limit: 5031, snapshot_date: '2026-06-28' },
        balances: { light: 'red', count: 3, red: 1 },
        clients: { light: 'amber', total_active: 6, new_count: 5, logged_in: 5, dormant: 1 },
    }),
    getDashboardFinance: vi.fn().mockResolvedValue({
        period: '7d',
        kpi: { topups_rub: '320000', charges_rub: '180000', net_inflow_rub: '140000', negative_balance_count: 1 },
        attention: [{ id: 9, subdomain: 'romashka', organization_name: 'ООО Ромашка', balance_rub: '-4200', state: 'negative' }],
        top_by_turnover: [{ id: 2, organization_name: 'lkomega', topped_rub: '200000' }],
    }),
    getDashboardHealth: vi.fn().mockResolvedValue({
        overall_light: 'green',
        subsystems: [
            { key: 'queues', light: 'green', detail: '0 упавших за сутки' },
            { key: 'incidents', light: 'green', detail: '0 открытых' },
        ],
    }),
    getDashboardLeads: vi.fn().mockResolvedValue({
        light: 'green',
        kpi: { delivered_today: 71, received_today: 80, stuck: 0, unrouted: 0 },
        recent: [
            { id: 501, received_at: '2026-06-28 07:55', platform: 'B1', channel: 'site', source: 'okna.ru', phone_masked: '79***07', delivered: true, processed: true },
        ],
    }),
    getDashboardSupply: vi.fn().mockResolvedValue({
        snapshot_date: '2026-06-28',
        light: 'red',
        totals: { demand: 250, formula: 160, ordered: 175, mismatches: 1 },
        total_orders: 405,
        total_limit: 5031,
        groups: [{ signal_type: 'site', identifier: 'okna.ru', demand: 150, formula: 100, ordered: 100, in_sync: true }],
    }),
    getDashboardClients: vi.fn().mockResolvedValue({
        kpi: { total_active: 6, new_count: 5, logged_in: 5, got_leads: 3, paid: 3 },
        new_clients: [
            { id: 11, organization_name: 'Новый Актив', subdomain: 'na', status: 'active', created_at: '2026-06-27', last_login_at: '2026-06-27 07:55', delivered_in_month: 3, balance_rub: '90' },
            { id: 12, organization_name: 'Не Активировался', subdomain: 'naktiv', status: 'active', created_at: '2026-06-23', last_login_at: null, delivered_in_month: 0, balance_rub: '300' },
        ],
        dormant: [
            { id: 12, organization_name: 'Не Активировался', subdomain: 'naktiv', last_login_at: null, balance_rub: '300' },
        ],
    }),
    getDashboardBalances: vi.fn().mockResolvedValue({
        light: 'red',
        services: [
            { service_key: 'yandex_cloud', balance_amount: '-540', currency: 'RUB', daily_spend_estimate: '600', days_left: 0, light: 'red', ok: true, error: null, checked_at: '2026-06-28T06:30:00Z', topup_url: 'https://console.yandex.cloud/billing/accounts/dn2w7fcvynjxe6elljct/payments' },
            { service_key: 'dadata', balance_amount: '4500', currency: 'RUB', daily_spend_estimate: '100', days_left: 9, light: 'green', ok: true, error: null, checked_at: '2026-06-28T06:30:00Z', topup_url: 'https://dadata.ru/profile/#billing' },
            { service_key: 'supplier', balance_amount: null, currency: 'RUB', daily_spend_estimate: null, days_left: null, light: 'grey', ok: false, error: 'кабинет недоступен', checked_at: '2026-06-28T06:30:00Z', topup_url: 'https://crm.bp-gr.ru' },
        ],
    }),
}));

beforeEach(() => {
    vi.clearAllMocks();
});

describe('AdminDashboardView.vue', () => {
    const factory = async () => {
        const router = createRouter({
            history: createMemoryHistory(),
            routes: [
                { path: '/admin/dashboard', name: 'admin-dashboard', component: AdminDashboardView },
                { path: '/admin/tenants/:code', name: 'admin-tenant-detail', component: { template: '<div />' } },
            ],
        });
        await router.push('/admin/dashboard');
        await router.isReady();
        const wrapper = mount(AdminDashboardView, {
            global: { plugins: [createVuetify(), router] },
        });
        await flushPromises();
        await wrapper.vm.$nextTick();
        return { wrapper, router };
    };

    it('монтируется и содержит заголовок «Командный центр»', async () => {
        const { wrapper } = await factory();
        expect(wrapper.find('h1').text()).toBe('Командный центр');
    });

    it('рендерит 6 плиток: Финансы / Здоровье / Лиды / Заказ / Балансы / Клиенты', async () => {
        const { wrapper } = await factory();
        const text = wrapper.text();
        expect(text).toContain('Финансы');
        expect(text).toContain('Здоровье портала');
        expect(text).toContain('Лиды');
        expect(text).toContain('Заказ у поставщика');
        expect(text).toContain('Балансы сервисов');
        expect(text).toContain('Клиенты');
    });

    it('плитка Клиенты показывает новых и спящих, drill — списки', async () => {
        const { wrapper } = await factory();
        expect(wrapper.text()).toContain('1 спят'); // dormant=1
        await wrapper.find('[data-testid="tile-clients"]').trigger('click');
        await wrapper.vm.$nextTick();
        expect(wrapper.find('[data-testid="drill-clients"]').exists()).toBe(true);
        expect(wrapper.text()).toContain('Не Активировался');
        expect(wrapper.text()).toContain('ни разу'); // last_login_at=null
    });

    it('плитка Балансы показывает сервисы и «на исходе»', async () => {
        const { wrapper } = await factory();
        const text = wrapper.text();
        expect(text).toContain('Yandex Cloud');
        expect(text).toContain('DaData');
        expect(text).toContain('1 на исходе'); // red=1
    });

    it('drill Балансов содержит кнопку «Пополнить» с прямой ссылкой на оплату', async () => {
        const { wrapper } = await factory();
        await wrapper.find('[data-testid="tile-balances"]').trigger('click');
        await wrapper.vm.$nextTick();
        expect(wrapper.find('[data-testid="drill-balances"]').exists()).toBe(true);
        const topup = wrapper.find('[data-testid="topup-yandex_cloud"]');
        expect(topup.exists()).toBe(true);
        expect(topup.attributes('href')).toBe('https://console.yandex.cloud/billing/accounts/dn2w7fcvynjxe6elljct/payments');
        // упавший сервис показан как «не удалось обновить»
        expect(wrapper.text()).toContain('не удалось обновить');
    });

    it('плитки Лиды и Заказ показывают живые числа', async () => {
        const { wrapper } = await factory();
        const text = wrapper.text();
        expect(text).toContain('Доставлено сегодня');
        expect(text).toContain('71');
        expect(text).toContain('1 рассинхрон'); // светофор Заказа (mismatches=1)
    });

    it('клик по плитке Заказ показывает таблицу групп', async () => {
        const { wrapper } = await factory();
        await wrapper.find('[data-testid="tile-supply"]').trigger('click');
        await wrapper.vm.$nextTick();
        expect(wrapper.find('[data-testid="drill-supply"]').exists()).toBe(true);
        expect(wrapper.text()).toContain('okna.ru');
        expect(wrapper.text()).toContain('По группам');
    });

    it('drill Лиды показывает последние лиды и ссылку «Открыть все лиды»', async () => {
        const { wrapper } = await factory();
        await wrapper.find('[data-testid="tile-leads"]').trigger('click');
        await wrapper.vm.$nextTick();
        expect(wrapper.find('[data-testid="drill-leads"]').exists()).toBe(true);
        expect(wrapper.text()).toContain('okna.ru'); // recent lead source
        const link = wrapper.find('[data-testid="open-all-leads"]');
        expect(link.exists()).toBe(true);
        expect(link.attributes('href')).toContain('/admin/leads');
    });

    it('Финансы и Здоровье показывают живые числа из API', async () => {
        const { wrapper } = await factory();
        const text = wrapper.text();
        expect(text).toMatch(/320\s+000\s*₽/); // пополнения
        expect(text).toContain('1 в минусе'); // светофор Финансов (red)
        expect(text).toContain('success'); // статус синхрона
    });

    it('по умолчанию открыт drill Финансов с KPI «Чистый приток»', async () => {
        const { wrapper } = await factory();
        expect(wrapper.find('[data-testid="drill-fin"]').exists()).toBe(true);
        expect(wrapper.text()).toMatch(/140\s+000\s*₽/); // net_inflow
        expect(wrapper.text()).toContain('ООО Ромашка'); // строка «внимание»
    });

    it('клик по плитке Здоровье переключает drill на подсистемы', async () => {
        const { wrapper } = await factory();
        await wrapper.find('[data-testid="tile-health"]').trigger('click');
        await wrapper.vm.$nextTick();
        expect(wrapper.find('[data-testid="drill-health"]').exists()).toBe(true);
        expect(wrapper.find('[data-testid="drill-fin"]').exists()).toBe(false);
        expect(wrapper.text()).toContain('Очереди / джобы');
    });

    it('клик по строке «внимание» уводит на карточку тенанта (Уровень 3)', async () => {
        const { wrapper, router } = await factory();
        const push = vi.spyOn(router, 'push');
        await wrapper.find('[data-testid="drill-fin"] tbody tr.clk').trigger('click');
        await wrapper.vm.$nextTick();
        expect(push).toHaveBeenCalledWith({ name: 'admin-tenant-detail', params: { code: 'romashka' } });
    });

    it('смена периода перезагружает данные (вызов summary дважды)', async () => {
        const { wrapper } = await factory();
        const api = await import('../../resources/js/api/adminDashboard');
        expect(api.getDashboardSummary).toHaveBeenCalledTimes(1);
        expect(api.getDashboardSummary).toHaveBeenLastCalledWith({ period: '7d' });
        await wrapper.find('[data-testid="period-30d"]').trigger('click');
        await flushPromises();
        expect(api.getDashboardSummary).toHaveBeenCalledTimes(2);
        expect(api.getDashboardSummary).toHaveBeenLastCalledWith({ period: '30d' });
    });

    it('свой период шлёт date_from/date_to', async () => {
        const { wrapper } = await factory();
        const api = await import('../../resources/js/api/adminDashboard');
        await wrapper.find('[data-testid="period-custom-toggle"]').trigger('click');
        await wrapper.vm.$nextTick();
        wrapper.vm.dateFrom = '2026-06-01';
        wrapper.vm.dateTo = '2026-06-15';
        await wrapper.vm.$nextTick();
        await wrapper.find('[data-testid="apply-custom"]').trigger('click');
        await flushPromises();
        expect(api.getDashboardSummary).toHaveBeenLastCalledWith({ date_from: '2026-06-01', date_to: '2026-06-15' });
    });

    it('API reject → fetch-error-alert виден', async () => {
        const api = await import('../../resources/js/api/adminDashboard');
        vi.mocked(api.getDashboardSummary).mockRejectedValueOnce(new Error('Network'));
        const { wrapper } = await factory();
        expect(wrapper.find('[data-testid="fetch-error-alert"]').exists()).toBe(true);
    });
});
