import { describe, it, expect, beforeEach, vi } from 'vitest';

vi.mock('../../resources/js/api/client', () => ({
    apiClient: {
        get: vi.fn(),
        post: vi.fn(),
        patch: vi.fn(),
        delete: vi.fn(),
    },
    ensureCsrfCookie: vi.fn(),
}));

import {
    createDeal,
    bulkDeleteDeals,
    bulkRestoreDeals,
    updateDeal,
    transitionDeals,
    exportDeals,
    exportDealsXlsx,
    getDeal,
    listDeals,
    listManagers,
    listProjects,
} from '../../resources/js/api/deals';
import { apiClient, ensureCsrfCookie } from '../../resources/js/api/client';

const FAKE_DEAL = {
    id: 1,
    tenant_id: 1,
    project_id: 1,
    project_name: 'Test',
    phone: '+79161234567',
    contact_name: 'X',
    status: 'new',
    manager_id: null,
    manager_name: null,
    manager_initials: null,
    received_at: null,
    comment: null,
    assigned_at: null,
};

describe('api/deals', () => {
    beforeEach(() => vi.clearAllMocks());

    it('createDeal() POSTs /api/deals + ensureCsrfCookie + unwraps data.deal', async () => {
        vi.mocked(apiClient.post).mockResolvedValue({ data: { deal: FAKE_DEAL, message: 'ok' } });
        const r = await createDeal({ tenant_id: 1, project_name: 'P', phone: '+79161234567' });
        expect(ensureCsrfCookie).toHaveBeenCalledOnce();
        expect(apiClient.post).toHaveBeenCalledWith('/api/deals', {
            tenant_id: 1,
            project_name: 'P',
            phone: '+79161234567',
        });
        expect(r.id).toBe(1);
    });

    it('bulkDeleteDeals() DELETE /api/deals с data payload', async () => {
        vi.mocked(apiClient.delete).mockResolvedValue({ data: { deleted: 3, requested: 4 } });
        const r = await bulkDeleteDeals({ tenant_id: 1, ids: [1, 2, 3, 4] });
        expect(ensureCsrfCookie).toHaveBeenCalledOnce();
        expect(apiClient.delete).toHaveBeenCalledWith('/api/deals', { data: { tenant_id: 1, ids: [1, 2, 3, 4] } });
        expect(r.deleted).toBe(3);
    });

    it('bulkRestoreDeals() POSTs /api/deals/restore', async () => {
        vi.mocked(apiClient.post).mockResolvedValue({ data: { restored: 2, requested: 2 } });
        await bulkRestoreDeals({ tenant_id: 1, ids: [1, 2] });
        expect(ensureCsrfCookie).toHaveBeenCalledOnce();
        expect(apiClient.post).toHaveBeenCalledWith('/api/deals/restore', { tenant_id: 1, ids: [1, 2] });
    });

    it('updateDeal() PATCH /api/deals/{id} c partial payload', async () => {
        vi.mocked(apiClient.patch).mockResolvedValue({ data: { deal: FAKE_DEAL } });
        await updateDeal(42, { tenant_id: 1, comment: 'new' });
        expect(ensureCsrfCookie).toHaveBeenCalledOnce();
        expect(apiClient.patch).toHaveBeenCalledWith('/api/deals/42', { tenant_id: 1, comment: 'new' });
    });

    it('transitionDeals() POSTs /api/deals/transition с status', async () => {
        vi.mocked(apiClient.post).mockResolvedValue({ data: { updated: 5, requested: 5, status: 'won' } });
        const r = await transitionDeals({ tenant_id: 1, ids: [1, 2, 3, 4, 5], status: 'won' });
        expect(apiClient.post).toHaveBeenCalledWith('/api/deals/transition', {
            tenant_id: 1,
            ids: [1, 2, 3, 4, 5],
            status: 'won',
        });
        expect(r.status).toBe('won');
    });

    it('exportDeals() POSTs /api/deals/export с format=csv + responseType=text', async () => {
        vi.mocked(apiClient.post).mockResolvedValue({ data: 'id,phone\n1,+79..' });
        const r = await exportDeals({ tenant_id: 1, ids: [1] });
        expect(ensureCsrfCookie).toHaveBeenCalledOnce();
        expect(apiClient.post).toHaveBeenCalledWith(
            '/api/deals/export',
            { tenant_id: 1, ids: [1], format: 'csv' },
            { responseType: 'text' },
        );
        expect(typeof r).toBe('string');
    });

    it('exportDealsXlsx() POSTs /api/deals/export с format=xlsx + responseType=blob', async () => {
        const blob = new Blob(['fake'], { type: 'application/octet-stream' });
        vi.mocked(apiClient.post).mockResolvedValue({ data: blob });
        const r = await exportDealsXlsx({ tenant_id: 1, ids: [1, 2] });
        expect(apiClient.post).toHaveBeenCalledWith(
            '/api/deals/export',
            { tenant_id: 1, ids: [1, 2], format: 'xlsx' },
            { responseType: 'blob' },
        );
        expect(r).toBeInstanceOf(Blob);
    });

    it('getDeal() GET /api/deals/{id} с tenant_id param', async () => {
        vi.mocked(apiClient.get).mockResolvedValue({ data: { deal: FAKE_DEAL, events: [] } });
        const r = await getDeal(7, 1);
        expect(apiClient.get).toHaveBeenCalledWith('/api/deals/7', { params: { tenant_id: 1 } });
        expect(r.events).toEqual([]);
    });

    it('listDeals() GET /api/deals с маппингом camelCase→snake_case + onlyDeleted flag', async () => {
        vi.mocked(apiClient.get).mockResolvedValue({
            data: { deals: [], total: 0, limit: 50, offset: 0 },
        });
        await listDeals({
            tenantId: 1,
            statusIn: ['new', 'in_progress'],
            projectId: 2,
            managerId: 3,
            search: 'q',
            limit: 50,
            offset: 0,
            onlyDeleted: true,
        });
        expect(apiClient.get).toHaveBeenCalledWith('/api/deals', {
            params: {
                tenant_id: 1,
                status_in: ['new', 'in_progress'],
                project_id: 2,
                manager_id: 3,
                search: 'q',
                limit: 50,
                offset: 0,
                only_deleted: 'true',
            },
        });
    });

    it('listDeals() без onlyDeleted → only_deleted=undefined', async () => {
        vi.mocked(apiClient.get).mockResolvedValue({ data: { deals: [], total: 0, limit: 50, offset: 0 } });
        await listDeals({ tenantId: 1 });
        expect(apiClient.get).toHaveBeenCalledWith(
            '/api/deals',
            expect.objectContaining({ params: expect.objectContaining({ only_deleted: undefined }) }),
        );
    });

    it('listManagers() GET /api/managers + unwraps data.managers', async () => {
        vi.mocked(apiClient.get).mockResolvedValue({
            data: {
                managers: [{ id: 1, email: 'm@x.ru', first_name: 'M', last_name: 'X', name: 'M X', initials: 'MX' }],
            },
        });
        const r = await listManagers(1);
        expect(apiClient.get).toHaveBeenCalledWith('/api/managers', { params: { tenant_id: 1 } });
        expect(r).toHaveLength(1);
    });

    it('listProjects() GET /api/projects + unwraps { data: [...] } (JsonResource collection)', async () => {
        // ProjectController::index() отдаёт response()->json(['data' => ProjectResource::collection(...)]).
        vi.mocked(apiClient.get).mockResolvedValue({
            data: {
                data: [
                    { id: 1, name: 'B1_Окна СПб' },
                    { id: 2, name: 'B2_Двери' },
                ],
            },
        });
        const r = await listProjects(1);
        expect(apiClient.get).toHaveBeenCalledWith('/api/projects', { params: { tenant_id: 1 } });
        expect(Array.isArray(r)).toBe(true);
        expect(r).toHaveLength(2);
        expect(r[0].name).toBe('B1_Окна СПб');
    });

    it('listProjects() возвращает [] при ответе без массива (защита от undefined.map)', async () => {
        vi.mocked(apiClient.get).mockResolvedValue({ data: {} });
        const r = await listProjects(1);
        expect(r).toEqual([]);
    });
});
