import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { mount } from '@vue/test-utils';
import { createVuetify } from 'vuetify';

import AdminPricingTiersView from '../../resources/js/views/admin/AdminPricingTiersView.vue';

/**
 * Создаёт объект, который проходит `axios.isAxiosError()` (проверяет флаг `isAxiosError: true`),
 * с нужным `response.data.message`.
 */
function makeAxiosError(message: string, status = 422): unknown {
    return Object.assign(new Error(message), {
        isAxiosError: true,
        response: { status, data: { message } },
    });
}

vi.mock('../../resources/js/api/admin', async (importOriginal) => {
    const orig = await importOriginal<typeof import('../../resources/js/api/admin')>();
    return {
        ...orig,
        getPricingTiers: vi.fn(),
        createPricingTiers: vi.fn(),
        deleteScheduledPricingTier: vi.fn(),
    };
});

const adminApi = await import('../../resources/js/api/admin');

// Auto-импорт компонентов/директив Vuetify подхватывает vite-plugin-vuetify
// из vitest.config.ts (см. AdminBillingView.spec.ts).
const vuetify = createVuetify();

const mockTiers = [
    { tier_no: 1, leads_in_tier: 100, price_per_lead_kopecks: 50000, effective_from: '1970-01-01' },
    { tier_no: 2, leads_in_tier: 200, price_per_lead_kopecks: 45000, effective_from: '1970-01-01' },
    { tier_no: 3, leads_in_tier: 400, price_per_lead_kopecks: 40000, effective_from: '1970-01-01' },
    { tier_no: 4, leads_in_tier: 800, price_per_lead_kopecks: 35000, effective_from: '1970-01-01' },
    { tier_no: 5, leads_in_tier: 1500, price_per_lead_kopecks: 30000, effective_from: '1970-01-01' },
    { tier_no: 6, leads_in_tier: 3000, price_per_lead_kopecks: 27000, effective_from: '1970-01-01' },
    { tier_no: 7, leads_in_tier: null, price_per_lead_kopecks: 25000, effective_from: '1970-01-01' },
];

describe('AdminPricingTiersView', () => {
    beforeEach(() => {
        vi.mocked(adminApi.getPricingTiers).mockResolvedValue({ active: mockTiers, scheduled: {} });
        vi.mocked(adminApi.createPricingTiers).mockResolvedValue({ effective_from: '2026-06-01' });
        vi.mocked(adminApi.deleteScheduledPricingTier).mockResolvedValue(undefined);
    });

    it('renders 7 tier rows from /api/admin/pricing-tiers', async () => {
        const wrapper = mount(AdminPricingTiersView, { global: { plugins: [vuetify] } });
        await new Promise((r) => setTimeout(r, 50));
        expect(wrapper.text()).toContain('500.00');
        expect(wrapper.text()).toContain('250.00');
    });

    it('shows "все свыше" for tier 7 with leads_in_tier=null', async () => {
        const wrapper = mount(AdminPricingTiersView, { global: { plugins: [vuetify] } });
        await new Promise((r) => setTimeout(r, 50));
        expect(wrapper.text()).toContain('все свыше');
    });

    it('opens editor dialog on button click', async () => {
        const wrapper = mount(AdminPricingTiersView, { global: { plugins: [vuetify] } });
        await new Promise((r) => setTimeout(r, 50));
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        expect((wrapper.vm as any).editorOpen).toBe(false);
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        (wrapper.vm as any).editorOpen = true;
        await wrapper.vm.$nextTick();
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        expect((wrapper.vm as any).editorOpen).toBe(true);
    });

    it('submits POST with editor payload', async () => {
        const wrapper = mount(AdminPricingTiersView, { global: { plugins: [vuetify] } });
        await new Promise((r) => setTimeout(r, 50));
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        await (wrapper.vm as any).submit();
        expect(adminApi.createPricingTiers).toHaveBeenCalledWith(
            expect.arrayContaining([expect.objectContaining({ tier_no: 7, leads_in_tier: null })]),
            expect.any(String),
        );
    });

    it('редактор содержит поле даты effective_from', async () => {
        const wrapper = mount(AdminPricingTiersView, {
            global: {
                plugins: [vuetify],
                stubs: { VDialog: { template: '<div><slot /></div>' } },
            },
        });
        await new Promise((r) => setTimeout(r, 50));
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        (wrapper.vm as any).editorOpen = true;
        await wrapper.vm.$nextTick();
        expect(wrapper.find('[data-testid="effective-from-input"]').exists()).toBe(true);
    });

    it('submit передаёт выбранную effective_from в createPricingTiers', async () => {
        const wrapper = mount(AdminPricingTiersView, { global: { plugins: [vuetify] } });
        await new Promise((r) => setTimeout(r, 50));
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        (wrapper.vm as any).effectiveFrom = '2026-09-01';
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        await (wrapper.vm as any).submit();
        expect(adminApi.createPricingTiers).toHaveBeenCalledWith(expect.any(Array), '2026-09-01');
    });

    it('confirmDelete открывает диалог подтверждения, DELETE не вызывается сразу', async () => {
        const wrapper = mount(AdminPricingTiersView, { global: { plugins: [vuetify] } });
        await new Promise((r) => setTimeout(r, 50));
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const vm = wrapper.vm as any;
        vm.confirmDelete('2026-06-01');
        expect(vm.deleteDialogOpen).toBe(true);
        expect(vm.deleteTarget).toBe('2026-06-01');
        expect(adminApi.deleteScheduledPricingTier).not.toHaveBeenCalled();
    });

    it('openEditor сбрасывает effectiveFrom к дефолту (nextMonthStart)', async () => {
        const wrapper = mount(AdminPricingTiersView, { global: { plugins: [vuetify] } });
        await new Promise((r) => setTimeout(r, 50));
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const vm = wrapper.vm as any;
        vm.effectiveFrom = '2099-01-01';
        vm.openEditor();
        expect(vm.editorOpen).toBe(true);
        expect(vm.effectiveFrom).not.toBe('2099-01-01');
    });

    it('performDelete вызывает deleteScheduledPricingTier для выбранной даты', async () => {
        const wrapper = mount(AdminPricingTiersView, { global: { plugins: [vuetify] } });
        await new Promise((r) => setTimeout(r, 50));
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const vm = wrapper.vm as any;
        vm.confirmDelete('2026-06-01');
        await vm.performDelete();
        expect(adminApi.deleteScheduledPricingTier).toHaveBeenCalledWith('2026-06-01');
        expect(vm.deleteDialogOpen).toBe(false);
    });
});

describe('AdminPricingTiersView error handling (Sprint 1 G1)', () => {
    afterEach(() => {
        vi.clearAllMocks();
    });

    it('submit() shows errorMessage when createPricingTiers rejects with 422', async () => {
        vi.mocked(adminApi.getPricingTiers).mockResolvedValue({ active: mockTiers, scheduled: {} });
        vi.mocked(adminApi.createPricingTiers).mockRejectedValue(
            makeAxiosError('Validation failed: tier 7 leads_in_tier must be null', 422),
        );
        const wrapper = mount(AdminPricingTiersView, { global: { plugins: [vuetify] } });
        await new Promise((r) => setTimeout(r, 50));
        // Open editor first (submit is called from dialog) so we can verify it stays open on error.
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        (wrapper.vm as any).editorOpen = true;
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        await (wrapper.vm as any).submit();
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const vm = wrapper.vm as any;
        expect(vm.errorMessage).toContain('Validation failed');
        expect(vm.saving).toBe(false);
        // Dialog should remain OPEN so user can fix and retry
        expect(vm.editorOpen).toBe(true);
    });

    it('submit() shows successMessage on 200', async () => {
        vi.mocked(adminApi.getPricingTiers).mockResolvedValue({ active: mockTiers, scheduled: {} });
        vi.mocked(adminApi.createPricingTiers).mockResolvedValue({ effective_from: '2026-06-01' });
        const wrapper = mount(AdminPricingTiersView, { global: { plugins: [vuetify] } });
        await new Promise((r) => setTimeout(r, 50));
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        await (wrapper.vm as any).submit();
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const vm = wrapper.vm as any;
        expect(vm.successMessage).toContain('Сохранено');
        expect(vm.errorMessage).toBe(null);
        expect(vm.saving).toBe(false);
        expect(vm.editorOpen).toBe(false);
        expect(vm.successToastOpen).toBe(true);
    });

    it('performDelete() shows errorMessage when deleteScheduledPricingTier rejects', async () => {
        vi.mocked(adminApi.getPricingTiers).mockResolvedValue({ active: mockTiers, scheduled: {} });
        vi.mocked(adminApi.deleteScheduledPricingTier).mockRejectedValue(
            makeAxiosError('Database connection failed', 500),
        );
        const wrapper = mount(AdminPricingTiersView, { global: { plugins: [vuetify] } });
        await new Promise((r) => setTimeout(r, 50));
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const vm = wrapper.vm as any;
        vm.confirmDelete('2026-06-01');
        await vm.performDelete();
        expect(vm.errorMessage).toContain('Database connection failed');
    });

    it('performDelete() shows successMessage on OK', async () => {
        vi.mocked(adminApi.getPricingTiers).mockResolvedValue({ active: mockTiers, scheduled: {} });
        vi.mocked(adminApi.deleteScheduledPricingTier).mockResolvedValue(undefined);
        const wrapper = mount(AdminPricingTiersView, { global: { plugins: [vuetify] } });
        await new Promise((r) => setTimeout(r, 50));
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const vm = wrapper.vm as any;
        vm.confirmDelete('2026-06-01');
        await vm.performDelete();
        expect(vm.successMessage).toContain('Удалено');
        expect(vm.successToastOpen).toBe(true);
    });
});
