"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vitest_1 = require("vitest");
const kie_client_js_1 = require("./kie-client.js");
const mockFetch = vitest_1.vi.fn();
vitest_1.vi.mock('undici', () => ({ fetch: (...args) => mockFetch(...args) }));
vitest_1.vi.mock('../../utils/logger.js', () => ({
    createModuleLogger: () => ({
        debug: vitest_1.vi.fn(),
        info: vitest_1.vi.fn(),
        warn: vitest_1.vi.fn(),
        error: vitest_1.vi.fn(),
    }),
}));
vitest_1.vi.mock('../../utils/config.js', () => ({
    config: { kieApiKey: 'test-kie-key' },
}));
function makeOkResponse(data) {
    return {
        ok: true,
        status: 200,
        statusText: 'OK',
        json: () => Promise.resolve({ code: 200, data }),
        text: () => Promise.resolve(JSON.stringify({ code: 200, data })),
    };
}
function makeErrorResponse(status, body = 'error') {
    return {
        ok: false,
        status,
        statusText: 'Error',
        json: () => Promise.resolve({ error: body }),
        text: () => Promise.resolve(body),
    };
}
const SAMPLE_TASK_RESPONSE = {
    taskId: 'task-abc-123',
    status: 'pending',
};
const SAMPLE_REQUEST = {
    model: 'flux-2',
    prompt: 'A portrait photo of a woman',
    aspectRatio: '1:1',
};
(0, vitest_1.describe)('KIEClient', () => {
    let client;
    (0, vitest_1.beforeEach)(() => {
        client = new kie_client_js_1.KIEClient('test-kie-key', { initialBackoffMs: 1 });
        mockFetch.mockReset();
    });
    (0, vitest_1.describe)('constructor', () => {
        (0, vitest_1.it)('allows empty API key for deferred configuration', () => {
            (0, vitest_1.expect)(new kie_client_js_1.KIEClient('')).toBeInstanceOf(kie_client_js_1.KIEClient);
        });
        (0, vitest_1.it)('creates instance with valid key', () => {
            (0, vitest_1.expect)(new kie_client_js_1.KIEClient('valid-key')).toBeInstanceOf(kie_client_js_1.KIEClient);
        });
    });
    (0, vitest_1.describe)('createTask', () => {
        (0, vitest_1.it)('sends correct POST request and returns response', async () => {
            mockFetch.mockResolvedValueOnce(makeOkResponse(SAMPLE_TASK_RESPONSE));
            const result = await client.createTask(SAMPLE_REQUEST);
            (0, vitest_1.expect)(result).toEqual(SAMPLE_TASK_RESPONSE);
            (0, vitest_1.expect)(mockFetch).toHaveBeenCalledOnce();
            const [url, options] = mockFetch.mock.calls[0];
            (0, vitest_1.expect)(url).toBe('https://api.kie.ai/api/v1/jobs/createTask');
            (0, vitest_1.expect)(options.method).toBe('POST');
            const headers = options.headers;
            (0, vitest_1.expect)(headers['Authorization']).toBe('Bearer test-kie-key');
            (0, vitest_1.expect)(headers['Content-Type']).toBe('application/json');
            const body = JSON.parse(options.body);
            (0, vitest_1.expect)(body.model).toBe('flux-2');
            (0, vitest_1.expect)(body.input.prompt).toBe('A portrait photo of a woman');
            (0, vitest_1.expect)(body.input.aspect_ratio).toBe('1:1');
        });
        (0, vitest_1.it)('sends all optional fields when provided', async () => {
            mockFetch.mockResolvedValueOnce(makeOkResponse(SAMPLE_TASK_RESPONSE));
            const fullRequest = {
                model: 'seedream-4-5',
                prompt: 'test prompt',
                negativePrompt: 'blurry, low quality',
                referenceImages: ['https://example.com/img1.jpg', 'https://example.com/img2.jpg'],
                aspectRatio: '16:9',
                webhookUrl: 'https://example.com/webhook',
                enableTranslation: true,
                seed: 42,
            };
            await client.createTask(fullRequest);
            const body = JSON.parse(mockFetch.mock.calls[0][1].body);
            (0, vitest_1.expect)(body.input.negative_prompt).toBe('blurry, low quality');
            (0, vitest_1.expect)(body.input.reference_images).toHaveLength(2);
            (0, vitest_1.expect)(body.callBackUrl).toBe('https://example.com/webhook');
            (0, vitest_1.expect)(body.input.enable_translation).toBe(true);
            (0, vitest_1.expect)(body.input.seed).toBe(42);
        });
        (0, vitest_1.it)('supports all valid models', async () => {
            const models = [
                'nano-banana-2', 'flux-2', 'seedream-4-5', 'z-image', '4o-image',
            ];
            for (const model of models) {
                mockFetch.mockResolvedValueOnce(makeOkResponse(SAMPLE_TASK_RESPONSE));
                await (0, vitest_1.expect)(client.createTask({ ...SAMPLE_REQUEST, model })).resolves.toEqual(SAMPLE_TASK_RESPONSE);
            }
        });
        (0, vitest_1.it)('supports all valid aspect ratios', async () => {
            const ratios = ['1:1', '16:9', '9:16', '4:3', '3:4'];
            for (const aspectRatio of ratios) {
                mockFetch.mockResolvedValueOnce(makeOkResponse(SAMPLE_TASK_RESPONSE));
                await (0, vitest_1.expect)(client.createTask({ ...SAMPLE_REQUEST, aspectRatio })).resolves.toEqual(SAMPLE_TASK_RESPONSE);
            }
        });
        (0, vitest_1.it)('allows up to 8 reference images', async () => {
            mockFetch.mockResolvedValueOnce(makeOkResponse(SAMPLE_TASK_RESPONSE));
            const request = {
                ...SAMPLE_REQUEST,
                referenceImages: Array.from({ length: 8 }, (_, i) => `https://example.com/img${i}.jpg`),
            };
            await (0, vitest_1.expect)(client.createTask(request)).resolves.toEqual(SAMPLE_TASK_RESPONSE);
        });
        (0, vitest_1.it)('rejects more than 8 reference images', async () => {
            const request = {
                ...SAMPLE_REQUEST,
                referenceImages: Array.from({ length: 9 }, (_, i) => `https://example.com/img${i}.jpg`),
            };
            await (0, vitest_1.expect)(client.createTask(request)).rejects.toThrow('referenceImages exceeds maximum of 8');
        });
        (0, vitest_1.it)('rejects empty prompt', async () => {
            await (0, vitest_1.expect)(client.createTask({ ...SAMPLE_REQUEST, prompt: '' })).rejects.toThrow('prompt is required');
        });
        (0, vitest_1.it)('rejects invalid model', async () => {
            await (0, vitest_1.expect)(client.createTask({ ...SAMPLE_REQUEST, model: 'invalid-model' })).rejects.toThrow('Invalid model');
        });
        (0, vitest_1.it)('rejects invalid aspect ratio', async () => {
            await (0, vitest_1.expect)(client.createTask({ ...SAMPLE_REQUEST, aspectRatio: '2:1' })).rejects.toThrow('Invalid aspectRatio');
        });
    });
    (0, vitest_1.describe)('getTaskStatus', () => {
        (0, vitest_1.it)('sends correct GET request with taskId', async () => {
            const completedResponse = {
                taskId: 'task-abc-123',
                status: 'completed',
                resultUrl: 'https://cdn.kie.ai/result.jpg',
            };
            mockFetch.mockResolvedValueOnce(makeOkResponse(completedResponse));
            const result = await client.getTaskStatus('task-abc-123');
            (0, vitest_1.expect)(result).toEqual(completedResponse);
            const [url, options] = mockFetch.mock.calls[0];
            (0, vitest_1.expect)(url).toBe('https://api.kie.ai/api/v1/jobs/recordInfo?taskId=task-abc-123');
            (0, vitest_1.expect)(options.method).toBe('GET');
            const headers = options.headers;
            (0, vitest_1.expect)(headers['Authorization']).toBe('Bearer test-kie-key');
        });
        (0, vitest_1.it)('encodes taskId in URL', async () => {
            mockFetch.mockResolvedValueOnce(makeOkResponse(SAMPLE_TASK_RESPONSE));
            await client.getTaskStatus('task with spaces');
            const [url] = mockFetch.mock.calls[0];
            (0, vitest_1.expect)(url).toBe('https://api.kie.ai/api/v1/jobs/recordInfo?taskId=task%20with%20spaces');
        });
        (0, vitest_1.it)('rejects empty taskId', async () => {
            await (0, vitest_1.expect)(client.getTaskStatus('')).rejects.toThrow('taskId is required');
        });
        (0, vitest_1.it)('returns failed task status with error', async () => {
            const failedResponse = {
                taskId: 'task-fail',
                status: 'failed',
                error: 'Content policy violation',
            };
            mockFetch.mockResolvedValueOnce(makeOkResponse(failedResponse));
            const result = await client.getTaskStatus('task-fail');
            (0, vitest_1.expect)(result.status).toBe('failed');
            (0, vitest_1.expect)(result.error).toBe('Content policy violation');
        });
    });
    (0, vitest_1.describe)('retry logic', () => {
        (0, vitest_1.it)('retries on 500 errors with exponential backoff', async () => {
            mockFetch
                .mockResolvedValueOnce(makeErrorResponse(500))
                .mockResolvedValueOnce(makeErrorResponse(502))
                .mockResolvedValueOnce(makeOkResponse(SAMPLE_TASK_RESPONSE));
            const result = await client.createTask(SAMPLE_REQUEST);
            (0, vitest_1.expect)(result).toEqual(SAMPLE_TASK_RESPONSE);
            (0, vitest_1.expect)(mockFetch).toHaveBeenCalledTimes(3);
        });
        (0, vitest_1.it)('retries on 429 rate limit errors', async () => {
            mockFetch
                .mockResolvedValueOnce(makeErrorResponse(429, 'rate limited'))
                .mockResolvedValueOnce(makeOkResponse(SAMPLE_TASK_RESPONSE));
            const result = await client.createTask(SAMPLE_REQUEST);
            (0, vitest_1.expect)(result).toEqual(SAMPLE_TASK_RESPONSE);
            (0, vitest_1.expect)(mockFetch).toHaveBeenCalledTimes(2);
        });
        (0, vitest_1.it)('throws immediately on 4xx client errors (not 429)', async () => {
            mockFetch.mockResolvedValueOnce(makeErrorResponse(400, 'bad request'));
            await (0, vitest_1.expect)(client.createTask(SAMPLE_REQUEST)).rejects.toThrow('KIE API error: 400');
            (0, vitest_1.expect)(mockFetch).toHaveBeenCalledOnce();
        });
        (0, vitest_1.it)('throws after exhausting all retries', async () => {
            mockFetch
                .mockResolvedValueOnce(makeErrorResponse(500))
                .mockResolvedValueOnce(makeErrorResponse(500))
                .mockResolvedValueOnce(makeErrorResponse(500));
            await (0, vitest_1.expect)(client.createTask(SAMPLE_REQUEST)).rejects.toThrow('KIE API error: 500');
            (0, vitest_1.expect)(mockFetch).toHaveBeenCalledTimes(3);
        });
        (0, vitest_1.it)('retries on network errors', async () => {
            mockFetch
                .mockRejectedValueOnce(new Error('fetch failed'))
                .mockResolvedValueOnce(makeOkResponse(SAMPLE_TASK_RESPONSE));
            const result = await client.createTask(SAMPLE_REQUEST);
            (0, vitest_1.expect)(result).toEqual(SAMPLE_TASK_RESPONSE);
            (0, vitest_1.expect)(mockFetch).toHaveBeenCalledTimes(2);
        });
        (0, vitest_1.it)('retries getTaskStatus on server errors', async () => {
            mockFetch
                .mockResolvedValueOnce(makeErrorResponse(503))
                .mockResolvedValueOnce(makeOkResponse(SAMPLE_TASK_RESPONSE));
            const result = await client.getTaskStatus('task-123');
            (0, vitest_1.expect)(result).toEqual(SAMPLE_TASK_RESPONSE);
            (0, vitest_1.expect)(mockFetch).toHaveBeenCalledTimes(2);
        });
    });
});
(0, vitest_1.describe)('createKIEClient', () => {
    (0, vitest_1.it)('creates client from config', async () => {
        const { createKIEClient } = await import('./kie-client.js');
        const client = createKIEClient();
        (0, vitest_1.expect)(client).toBeInstanceOf(kie_client_js_1.KIEClient);
    });
});
//# sourceMappingURL=kie-client.test.js.map