import { describe, it, expect, vi, beforeEach } from 'vitest';
import { mount, flushPromises } from '@vue/test-utils';
import { createVuetify } from 'vuetify';
import { createPinia, setActivePinia } from 'pinia';
import DashboardView from '../../resources/js/views/DashboardView.vue';
import type { DashboardSummary } from '../../resources/js/api/dashboard';
import { useAuthStore } from '../../resources/js/stores/auth';
import type { AuthUser } from '../../resources/js/api/auth';

vi.mock('../../resources/js/api/dashboard', () => ({
    getDashboardSummary: vi.fn(),
}));

const mockUser: AuthUser = {
    id: 1,
    email: 'user@liderra.ru',
    first_name: 'Иван',
    last_name: 'Петров',
    tenant_id: 1,
    totp_enabled: false,
    last_login_at: null,
};

const dashboardApi = await import('../../resources/js/api/dashboard');

function makeSummary(overrides: Partial<DashboardSummary> = {}): DashboardSummary {
    return {
        range: '7d',
        leads_received: { value: 247, delta_pct: 12.3, delta_dir: 'up' },
        conversion: { value: 18.4, delta_pp: 2.1, delta_dir: 'up' },
        active_projects: { active: 8 },
        balance: { amount_rub: '14250.00', runway_days: 4, runway_leads: 285 },
        activity: { points: [3, 5, 2, 8, 6, 9, 4], labels: ['сб', 'вс', 'пн', 'вт', 'ср', 'чт', 'сегодня'], max: 10 },
        funnel: { new: 18, won: 45 },
        avg_lead_cost_rub: null,
        ...overrides,
    };
}

const mountView = () => {
    setActivePinia(createPinia());
    useAuthStore().user = mockUser;
    return mount(DashboardView, { global: { plugins: [createVuetify()] } });
};

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

describe('DashboardView.vue ↔ /api/dashboard/summary', () => {
    it('getDashboardSummary вызывается на mount', async () => {
        vi.mocked(dashboardApi.getDashboardSummary).mockResolvedValueOnce(makeSummary());
        mountView();
        await flushPromises();
        expect(dashboardApi.getDashboardSummary).toHaveBeenCalledTimes(1);
    });

    it('успех — KPI и баланс из API видны', async () => {
        vi.mocked(dashboardApi.getDashboardSummary).mockResolvedValueOnce(
            makeSummary({ balance: { amount_rub: '99000.00', runway_days: 9, runway_leads: 500 } }),
        );
        const wrapper = mountView();
        await flushPromises();
        const text = wrapper.text();
        expect(text).toContain('Получено лидов');
        expect(text).toContain('Конверсия в оплату');
        expect(text).toContain('Активные проекты');
        expect(text).toContain('Баланс');
        expect(text).toContain('99 000');
        expect(wrapper.text()).toContain('12.3%');
    });

    it('ошибка API — fallback на mock, view не падает', async () => {
        vi.mocked(dashboardApi.getDashboardSummary).mockRejectedValueOnce(new Error('500'));
        const wrapper = mountView();
        await flushPromises();
        expect(wrapper.text()).toContain('Получено лидов');
        expect(wrapper.find('.runway-fill').exists()).toBe(true);
        expect(wrapper.find('[data-testid="dashboard-fetch-error"]').exists()).toBe(true);
    });

    it('смена range перезапрашивает summary', async () => {
        vi.mocked(dashboardApi.getDashboardSummary).mockResolvedValue(makeSummary());
        const wrapper = mountView();
        await flushPromises();
        expect(dashboardApi.getDashboardSummary).toHaveBeenCalledTimes(1);
        (wrapper.vm as unknown as { range: string }).range = '30d';
        await flushPromises();
        expect(dashboardApi.getDashboardSummary).toHaveBeenCalledTimes(2);
    });

    // F3 (17.06.2026): runway_days в тексте — реальное число, не срезанное до
    // RUNWAY_MAX (7 сегментов полосы). Иначе дашборд расходится с биллингом.
    it('показывает реальный runway_days (не срезанный до 7 сегментов полосы)', async () => {
        vi.mocked(dashboardApi.getDashboardSummary).mockResolvedValueOnce(
            makeSummary({ balance: { amount_rub: '14250.00', runway_days: 28, runway_leads: 28 } }),
        );
        const wrapper = mountView();
        await flushPromises();
        expect(wrapper.text()).toContain('28 дней');
    });
});

describe('DashboardView — косяк 07: онбординг новичка', () => {
    beforeEach(() => localStorage.clear());

    it('не показывает онбординг, когда есть активные проекты', async () => {
        vi.mocked(dashboardApi.getDashboardSummary).mockResolvedValueOnce(makeSummary());
        const w = mountView();
        await flushPromises();
        expect(w.find('[data-testid="dashboard-onboarding"]').exists()).toBe(false);
    });

    it('показывает онбординг новичку без проектов и лидов', async () => {
        vi.mocked(dashboardApi.getDashboardSummary).mockResolvedValueOnce(
            makeSummary({
                active_projects: { active: 0 },
                leads_received: { value: 0, delta_pct: 0, delta_dir: 'neutral' },
            }),
        );
        const w = mountView();
        await flushPromises();
        const card = w.find('[data-testid="dashboard-onboarding"]');
        expect(card.exists()).toBe(true);
        expect(card.text()).toContain('Создать первый проект');
    });

    it('скрывает онбординг после «скрыть» и помнит это в localStorage', async () => {
        vi.mocked(dashboardApi.getDashboardSummary).mockResolvedValueOnce(
            makeSummary({
                active_projects: { active: 0 },
                leads_received: { value: 0, delta_pct: 0, delta_dir: 'neutral' },
            }),
        );
        const w = mountView();
        await flushPromises();
        expect(w.find('[data-testid="dashboard-onboarding"]').exists()).toBe(true);
        await w.find('[data-testid="onboarding-dismiss"]').trigger('click');
        await w.vm.$nextTick();
        expect(w.find('[data-testid="dashboard-onboarding"]').exists()).toBe(false);
        expect(localStorage.getItem('dashboard.onboardingDismissed')).toBe('1');
    });
});
