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

vi.mock('../../resources/js/api/deals', () => ({
    createDeal: vi.fn(),
    listProjects: vi.fn(() => Promise.resolve([])),
    listManagers: vi.fn(() => Promise.resolve([])),
}));
vi.mock('../../resources/js/api/client', () => ({
    extractErrorMessage: vi.fn((_e, fb?: string) => fb ?? 'err'),
    extractValidationErrors: vi.fn(() => null),
    apiClient: {},
    ensureCsrfCookie: vi.fn(),
}));

import * as dealsApi from '../../resources/js/api/deals';
import NewDealDialog from '../../resources/js/components/deals/NewDealDialog.vue';

const factory = (props: { modelValue: boolean; presetStatus?: string; tenantId?: number } = { modelValue: true }) =>
    mount(NewDealDialog, {
        props,
        global: {
            plugins: [createVuetify()],
            stubs: {
                VDialog: {
                    template: '<div class="dialog-stub" v-if="modelValue"><slot /></div>',
                    props: ['modelValue'],
                },
            },
        },
    });

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

describe('NewDealDialog.vue', () => {
    it('не рендерит content при modelValue=false', () => {
        const wrapper = factory({ modelValue: false });
        expect(wrapper.find('.dialog-stub').exists()).toBe(false);
    });

    it('рендерит 6 полей: name/phone/project/manager/cost/status + кнопки', () => {
        const wrapper = factory();
        const text = wrapper.text();
        expect(text).toContain('Новая сделка');
        ['field-name', 'field-phone', 'field-project', 'field-manager', 'field-cost', 'field-status'].forEach((id) => {
            expect(wrapper.find(`[data-testid="${id}"]`).exists()).toBe(true);
        });
        expect(wrapper.find('[data-testid="submit-btn"]').exists()).toBe(true);
        expect(wrapper.find('[data-testid="cancel-btn"]').exists()).toBe(true);
    });

    it('submit без данных не emits created — только validation errors', async () => {
        const wrapper = factory();
        await wrapper.find('[data-testid="submit-btn"]').trigger('click');
        await flushPromises();
        expect(wrapper.emitted('created')).toBeUndefined();
        // Errors отображаются (хотя бы для name/phone/project/manager)
        const text = wrapper.text();
        expect(text).toContain('Имя обязательно');
        expect(text).toContain('Телефон обязателен');
    });

    it('submit с валидными данными emits created с правильным deal + закрывает dialog', async () => {
        const wrapper = factory();
        const vm = wrapper.vm as unknown as {
            name: string;
            phone: string;
            project: string;
            manager: { initials: string; name: string };
            cost: number;
            statusSlug: string;
        };
        // Заполняем через прямой доступ к ref (компонент использует script setup)
        vm.name = 'Тест Тестов';
        vm.phone = '+7 (999) 123-45-67';
        vm.project = 'Окна Москва';
        vm.manager = { initials: 'ИП', name: 'Иван П.' };
        vm.cost = 1500;
        vm.statusSlug = 'new';
        await flushPromises();

        await wrapper.find('[data-testid="submit-btn"]').trigger('click');
        await flushPromises();

        const created = wrapper.emitted('created');
        expect(created).toBeDefined();
        const deal = (created![0] as unknown[])[0] as {
            name: string;
            phone: string;
            statusSlug: string;
            cost: number;
            project: string;
        };
        expect(deal.name).toBe('Тест Тестов');
        expect(deal.phone).toBe('+7 (999) 123-45-67');
        expect(deal.cost).toBe(1500);
        expect(deal.project).toBe('Окна Москва');
        expect(deal.statusSlug).toBe('new');

        const close = wrapper.emitted('update:modelValue');
        expect(close).toBeDefined();
        expect(close![0]).toEqual([false]);
    });

    it('phone < 10 цифр → validation error «Минимум 10 цифр»', async () => {
        const wrapper = factory();
        const vm = wrapper.vm as unknown as {
            name: string;
            phone: string;
            project: string;
            manager: { initials: string; name: string };
        };
        vm.name = 'X';
        vm.phone = '123';
        vm.project = 'Окна Москва';
        vm.manager = { initials: 'X', name: 'X' };
        await flushPromises();
        await wrapper.find('[data-testid="submit-btn"]').trigger('click');
        await flushPromises();
        expect(wrapper.emitted('created')).toBeUndefined();
        expect(wrapper.text()).toContain('Минимум 10 цифр');
    });

    it('presetStatus → statusSlug дефолтит на пресет (для KanbanView)', async () => {
        const wrapper = factory({ modelValue: true, presetStatus: 'won' });
        await flushPromises();
        const vm = wrapper.vm as unknown as { statusSlug: string };
        expect(vm.statusSlug).toBe('won');
    });

    it('без tenantId — submit НЕ вызывает API (local-only mode)', async () => {
        const wrapper = factory({ modelValue: true }); // tenantId не передан
        const vm = wrapper.vm as unknown as {
            name: string;
            phone: string;
            project: string;
            manager: { initials: string; name: string };
            statusSlug: string;
        };
        vm.name = 'X';
        vm.phone = '+7 (999) 000-00-00';
        vm.project = 'Окна Москва';
        vm.manager = { initials: 'X', name: 'X' };
        vm.statusSlug = 'new';
        await flushPromises();
        await wrapper.find('[data-testid="submit-btn"]').trigger('click');
        await flushPromises();
        expect(dealsApi.createDeal).not.toHaveBeenCalled();
        expect(wrapper.emitted('created')).toBeDefined();
    });

    it('с tenantId + успешный backend — emits deal с backend-id', async () => {
        vi.mocked(dealsApi.createDeal).mockResolvedValue({
            id: 4242,
            tenant_id: 1,
            project_id: 7,
            phone: '+7 (999) 000-00-00',
            status: 'new',
            contact_name: 'X',
            manager_id: null,
            received_at: '2026-05-09T12:00:00Z',
        });

        const wrapper = factory({ modelValue: true, tenantId: 1 });
        const vm = wrapper.vm as unknown as {
            name: string;
            phone: string;
            project: string;
            manager: { initials: string; name: string };
            statusSlug: string;
        };
        vm.name = 'X';
        vm.phone = '+7 (999) 000-00-00';
        vm.project = 'Окна Москва';
        vm.manager = { initials: 'X', name: 'X' };
        vm.statusSlug = 'new';
        await flushPromises();
        await wrapper.find('[data-testid="submit-btn"]').trigger('click');
        await flushPromises();

        expect(dealsApi.createDeal).toHaveBeenCalledWith({
            tenant_id: 1,
            project_name: 'Окна Москва',
            phone: '+7 (999) 000-00-00',
            contact_name: 'X',
            status: 'new',
        });
        const emitted = wrapper.emitted('created');
        expect(emitted).toBeDefined();
        const deal = (emitted![0] as unknown[])[0] as { id: number };
        expect(deal.id).toBe(4242); // backend-id, не local nextId()
    });

    it('с tenantId — loadLookups вызывает listManagers + listProjects на open', async () => {
        vi.mocked(dealsApi.listProjects).mockResolvedValue([
            { id: 7, name: 'Окна Москва', tag: null, type: 'webhook' },
        ]);
        vi.mocked(dealsApi.listManagers).mockResolvedValue([
            { id: 42, email: 'iv@ex.ru', first_name: 'Иван', last_name: 'Петров', name: 'Иван П.', initials: 'ИП' },
        ]);

        const wrapper = factory({ modelValue: true, tenantId: 1 });
        await flushPromises();

        expect(dealsApi.listProjects).toHaveBeenCalledWith(1);
        expect(dealsApi.listManagers).toHaveBeenCalledWith(1);

        // projectOptions / managerOptions заменены backend'ом
        const vm = wrapper.vm as unknown as {
            projectOptions: string[];
            managerOptions: Array<{ name: string; initials: string }>;
            managerIdByName: Map<string, number>;
        };
        expect(vm.projectOptions).toEqual(['Окна Москва']);
        expect(vm.managerOptions).toEqual([{ name: 'Иван П.', initials: 'ИП' }]);
        expect(vm.managerIdByName.get('Иван П.')).toBe(42);
    });

    it('submit с manager → передаёт backend manager_id из mapping', async () => {
        vi.mocked(dealsApi.listManagers).mockResolvedValue([
            { id: 99, email: 'x@y.ru', first_name: 'X', last_name: 'Y', name: 'X Y.', initials: 'XY' },
        ]);
        vi.mocked(dealsApi.createDeal).mockResolvedValue({
            id: 1,
            tenant_id: 1,
            project_id: 1,
            phone: '+7 (999) 000-00-00',
            status: 'new',
            contact_name: 'Z',
            manager_id: 99,
            received_at: '2026-05-09T12:00:00Z',
        });

        const wrapper = factory({ modelValue: true, tenantId: 1 });
        await flushPromises();
        const vm = wrapper.vm as unknown as {
            name: string;
            phone: string;
            project: string;
            manager: { initials: string; name: string };
            statusSlug: string;
        };
        vm.name = 'Z';
        vm.phone = '+7 (999) 000-00-00';
        vm.project = 'Окна Москва';
        vm.manager = { initials: 'XY', name: 'X Y.' };
        vm.statusSlug = 'new';
        await flushPromises();
        await wrapper.find('[data-testid="submit-btn"]').trigger('click');
        await flushPromises();

        expect(dealsApi.createDeal).toHaveBeenCalledWith(
            expect.objectContaining({
                manager_id: 99,
            }),
        );
    });

    it('с tenantId + backend-error — fallback на local-id + warning + emit', async () => {
        vi.mocked(dealsApi.createDeal).mockRejectedValue(new Error('Network down'));

        const wrapper = factory({ modelValue: true, tenantId: 1 });
        const vm = wrapper.vm as unknown as {
            name: string;
            phone: string;
            project: string;
            manager: { initials: string; name: string };
            statusSlug: string;
            submitError: string | null;
        };
        vm.name = 'X';
        vm.phone = '+7 (999) 000-00-00';
        vm.project = 'Окна Москва';
        vm.manager = { initials: 'X', name: 'X' };
        vm.statusSlug = 'new';
        await flushPromises();
        await wrapper.find('[data-testid="submit-btn"]').trigger('click');
        await flushPromises();

        // Warning показывается, deal эмитится с local-id, диалог остаётся открытым.
        expect(wrapper.find('[data-testid="submit-error-alert"]').exists()).toBe(true);
        expect(wrapper.emitted('created')).toBeDefined();
        // dialog НЕ закрывается на error
        const closeEmits = wrapper.emitted('update:modelValue');
        expect(closeEmits === undefined || !closeEmits.some((e) => e[0] === false)).toBe(true);
    });

    it('I3: без tenantId — projectOptions и managerOptions пусты (нет mock-fallback)', async () => {
        const wrapper = factory({ modelValue: true }); // нет tenantId
        await flushPromises();
        const vm = wrapper.vm as unknown as {
            projectOptions: string[];
            managerOptions: unknown[];
        };
        expect(vm.projectOptions).toHaveLength(0);
        expect(vm.managerOptions).toHaveLength(0);
    });

    it('C6: при провале loadLookups показывает degradation-alert', async () => {
        vi.spyOn(dealsApi, 'listProjects').mockRejectedValue(new Error('network'));
        vi.spyOn(dealsApi, 'listManagers').mockRejectedValue(new Error('network'));

        const wrapper = factory({ modelValue: true, tenantId: 7 });
        await flushPromises();

        expect(wrapper.vm.lookupsFailed).toBe(true);
        expect(wrapper.find('[data-testid="lookups-error-alert"]').exists()).toBe(true);
    });

    it('C6: при успешном loadLookups alert отсутствует', async () => {
        vi.mocked(dealsApi.listProjects).mockResolvedValue([{ id: 1, name: 'P', tag: null, type: 'manual' }]);
        vi.mocked(dealsApi.listManagers).mockResolvedValue([
            { id: 1, email: 'a@b.c', first_name: 'A', last_name: 'B', name: 'A B', initials: 'AB' },
        ]);

        const wrapper = factory({ modelValue: true, tenantId: 7 });
        await flushPromises();

        expect(wrapper.vm.lookupsFailed).toBe(false);
        expect(wrapper.find('[data-testid="lookups-error-alert"]').exists()).toBe(false);
    });
});
