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 DealDetailBody from '../../resources/js/components/deals/DealDetailBody.vue';
import { MOCK_DEALS } from '../../resources/js/composables/mockDeals';
import type { GetDealResponse, ApiDealEvent } from '../../resources/js/api/deals';

vi.mock('../../resources/js/api/deals', async (importOriginal) => {
    const orig = await importOriginal<typeof import('../../resources/js/api/deals')>();
    return {
        ...orig,
        getDeal: vi.fn(),
        updateDeal: vi.fn(),
    };
});

const dealsApi = await import('../../resources/js/api/deals');

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

const factory = (props: { tenantId?: number }) =>
    mount(DealDetailBody, {
        props: { deal: MOCK_DEALS[0], ...props },
        global: {
            plugins: [createVuetify()],
        },
    });

function makeApiEvent(overrides: Partial<ApiDealEvent> = {}): ApiDealEvent {
    return {
        id: 100,
        event: 'deal.created',
        context: { source: 'webhook' },
        created_at: new Date(Date.now() - 30 * 60 * 1000).toISOString(),
        actor: null,
        ...overrides,
    };
}

describe('DealDetailBody ↔ GET /api/deals/{id} integration', () => {
    it('БЕЗ tenantId — getDeal не вызывается, events пуст (I3)', async () => {
        const wrapper = factory({});
        await flushPromises();

        expect(dealsApi.getDeal).not.toHaveBeenCalled();
        const items = wrapper.findAll('.timeline-item');
        expect(items).toHaveLength(0);
    });

    it('С tenantId — getDeal вызывается, events заменяются на API', async () => {
        const apiResponse: GetDealResponse = {
            deal: {
                id: MOCK_DEALS[0].id,
                tenant_id: 1,
                project_id: 5,
                project_name: 'Окна Москва',
                phone: '+7 (999) 100-00-01',
                contact_name: 'Anna',
                comment: null,
                city: null,
                project_signal_type: null,
                status: 'new',
                manager_id: null,
                manager_name: null,
                manager_initials: null,
                received_at: new Date().toISOString(),
                assigned_at: null,
            },
            events: [
                makeApiEvent({ id: 1, event: 'deal.created', context: { source: 'webhook' } }),
                makeApiEvent({
                    id: 2,
                    event: 'deal.status_changed',
                    context: { from: 'new', to: 'won' },
                    actor: { id: 1, name: 'Иван П.', initials: 'ИП' },
                }),
            ],
        };
        vi.mocked(dealsApi.getDeal).mockResolvedValueOnce(apiResponse);

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

        expect(dealsApi.getDeal).toHaveBeenCalledWith(MOCK_DEALS[0].id, 1);
        const items = wrapper.findAll('.timeline-item');
        expect(items).toHaveLength(2);
        // status_changed event имеет detail "new → won".
        expect(wrapper.text()).toContain('new → won');
    });

    it('getDeal reject → eventsFetchError=true, alert виден, events пуст (I3)', async () => {
        vi.mocked(dealsApi.getDeal).mockRejectedValueOnce(new Error('500'));

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

        const vm = wrapper.vm as unknown as { eventsFetchError: boolean };
        expect(vm.eventsFetchError).toBe(true);
        expect(wrapper.find('[data-testid="events-fetch-error-alert"]').exists()).toBe(true);
        // I3: нет mock-fallback — events пуст.
        const items = wrapper.findAll('.timeline-item');
        expect(items).toHaveLength(0);
    });

    it('saveComment вызывает updateDeal + toast success + reload events', async () => {
        const apiResponse: GetDealResponse = {
            deal: {
                id: MOCK_DEALS[0].id,
                tenant_id: 1,
                project_id: 5,
                project_name: 'Окна',
                phone: '+7 (999) 100-00-01',
                contact_name: 'Anna',
                comment: 'old',
                city: null,
                project_signal_type: null,
                status: 'new',
                manager_id: null,
                manager_name: null,
                manager_initials: null,
                received_at: new Date().toISOString(),
                assigned_at: null,
            },
            events: [],
        };
        vi.mocked(dealsApi.getDeal).mockResolvedValue(apiResponse);
        vi.mocked(dealsApi.updateDeal).mockResolvedValueOnce({
            ...apiResponse.deal,
            comment: 'новая заметка',
        });

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

        const vm = wrapper.vm as unknown as {
            commentDraft: string;
            saveComment: () => Promise<void>;
            commentToastOpen: boolean;
            commentToastText: string;
            commentSaveError: boolean;
        };
        expect(vm.commentDraft).toBe('old'); // загружено из getDeal
        vm.commentDraft = 'новая заметка';
        await vm.saveComment();
        await flushPromises();

        expect(dealsApi.updateDeal).toHaveBeenCalledWith(MOCK_DEALS[0].id, {
            tenant_id: 1,
            comment: 'новая заметка',
        });
        expect(vm.commentToastOpen).toBe(true);
        expect(vm.commentToastText).toContain('сохранён');
        expect(vm.commentSaveError).toBe(false);
        // reload events после save — getDeal вызвался ещё раз.
        expect(dealsApi.getDeal).toHaveBeenCalledTimes(2);
    });

    it('saveComment reject → toast warning + commentSaveError=true', async () => {
        vi.mocked(dealsApi.getDeal).mockResolvedValueOnce({
            deal: {
                id: MOCK_DEALS[0].id,
                tenant_id: 1,
                project_id: 5,
                project_name: null,
                phone: '+7 (999)',
                contact_name: null,
                comment: null,
                city: null,
                project_signal_type: null,
                status: 'new',
                manager_id: null,
                manager_name: null,
                manager_initials: null,
                received_at: null,
                assigned_at: null,
            },
            events: [],
        });
        vi.mocked(dealsApi.updateDeal).mockRejectedValueOnce(new Error('500'));

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

        const vm = wrapper.vm as unknown as {
            commentDraft: string;
            saveComment: () => Promise<void>;
            commentSaveError: boolean;
            commentToastText: string;
        };
        vm.commentDraft = 'новый';
        await vm.saveComment();
        await flushPromises();

        expect(vm.commentSaveError).toBe(true);
        expect(vm.commentToastText).toContain('Не удалось');
    });

    it('comment-section не показывается без tenantId (read-only mode)', async () => {
        const wrapper = factory({});
        await flushPromises();
        expect(wrapper.find('[data-testid="comment-section"]').exists()).toBe(false);
    });
});
