"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vitest_1 = require("vitest");
const quality_control_js_1 = require("./quality-control.js");
// --- Mocks ---
const mockDna = {
    id: 'char-1',
    createdAt: '2025-01-01T00:00:00Z',
    updatedAt: '2025-01-01T00:00:00Z',
    status: 'active',
    identity: { name: 'Test', age: 25, nationality: 'TR', city: 'Istanbul', occupation: 'Model', personality_tags: [], mbti: 'ENFP', backstory: '' },
    physicalMeasurements: { height_cm: 170, weight_kg: 55, body_type: 'slim', measurements: { bust: 85, waist: 60, hips: 90 } },
    skinMap: { base: '#F5D0A9', shadow: '#D4A574', highlight: '#FFE4C4' },
    faceAnatomy: { eye_shape: 'almond', eye_color: '#8B4513', eyebrow_shape: 'arched', nose_type: 'straight', lip_shape: 'full', cheekbones: 'high', jawline: 'soft', distinctive_marks: [] },
    hairDNA: { style: 'wavy', length: 'long', texture: 'silky', root_color: '#3B2F2F', mid_color: '#5C4033', highlight_color: '#8B6914' },
    bodyBiometrics: { body_type_detail: 'ectomorph', posture: 'upright', skin_texture: 'smooth' },
    profileView: { best_angle: 'left-45', signature_pose: 'hand-on-hip' },
    expressionMotion: { default_expression: 'soft-smile', smile_type: 'closed-lip', gaze_style: 'direct' },
    makeupStyle: { philosophy: 'natural', skin_finish: 'dewy', colors: { primary: '#C48A6A', secondary: '#B5651D', accent: '#FF6347' } },
    fashionStyle: { aesthetic: 'casual-chic', main_colors: ['#FFFFFF', '#000000'], signature_pieces: ['denim jacket'] },
    technicalSettings: { camera_body: 'Sony A7IV', portrait_lens: '85mm f/1.4', full_body_lens: '35mm f/1.8', film_stock: 'Portra 400' },
};
function makeReport(overrides = {}) {
    return {
        imageId: 'img-1',
        characterId: 'char-1',
        taskId: 'task-1',
        scores: { characterAccuracy: 8, bodyConsistency: 7, environmentQuality: 7, overallAesthetic: 8 },
        totalScore: 7.6,
        passed: true,
        feedback: 'Good quality',
        createdAt: new Date().toISOString(),
        ...overrides,
    };
}
const mockAnalyzeImage = vitest_1.vi.fn();
const mockVlm = { analyzeImage: mockAnalyzeImage };
vitest_1.vi.mock('../data-store/index.js', () => ({
    dataStore: {
        getCharacter: vitest_1.vi.fn(),
        getQualityReportsDir: vitest_1.vi.fn(),
    },
}));
vitest_1.vi.mock('../data-store/file-ops.js', () => ({
    atomicWriteJSON: vitest_1.vi.fn(),
}));
const index_js_1 = require("../data-store/index.js");
const file_ops_js_1 = require("../data-store/file-ops.js");
const mockedDataStore = vitest_1.vi.mocked(index_js_1.dataStore);
const mockedAtomicWrite = vitest_1.vi.mocked(file_ops_js_1.atomicWriteJSON);
(0, vitest_1.beforeEach)(() => {
    vitest_1.vi.clearAllMocks();
    mockedDataStore.getCharacter.mockResolvedValue(mockDna);
    mockedDataStore.getQualityReportsDir.mockReturnValue('/data/characters/char-1/quality-reports');
});
(0, vitest_1.describe)('QualityController', () => {
    (0, vitest_1.describe)('checkQuality', () => {
        (0, vitest_1.it)('should run VLM analysis and save the report', async () => {
            const report = makeReport();
            mockAnalyzeImage.mockResolvedValue(report);
            const controller = new quality_control_js_1.QualityController(mockVlm);
            const result = await controller.checkQuality('http://img.test/1.jpg', 'char-1', 'img-1', 'task-1');
            (0, vitest_1.expect)(mockAnalyzeImage).toHaveBeenCalledWith('http://img.test/1.jpg', mockDna, 'img-1', 'task-1', 7.0);
            (0, vitest_1.expect)(mockedAtomicWrite).toHaveBeenCalledTimes(1);
            const writtenPath = mockedAtomicWrite.mock.calls[0][0];
            (0, vitest_1.expect)(writtenPath).toContain('quality-reports');
            (0, vitest_1.expect)(writtenPath).toContain('img-1.json');
            (0, vitest_1.expect)(mockedAtomicWrite.mock.calls[0][1]).toEqual(report);
            (0, vitest_1.expect)(result).toEqual(report);
        });
        (0, vitest_1.it)('should use custom threshold when provided', async () => {
            const report = makeReport({ totalScore: 5.0, passed: false });
            mockAnalyzeImage.mockResolvedValue(report);
            const controller = new quality_control_js_1.QualityController(mockVlm);
            await controller.checkQuality('http://img.test/1.jpg', 'char-1', 'img-1', 'task-1', 8.0);
            (0, vitest_1.expect)(mockAnalyzeImage).toHaveBeenCalledWith('http://img.test/1.jpg', mockDna, 'img-1', 'task-1', 8.0);
        });
        (0, vitest_1.it)('should throw if character not found', async () => {
            mockedDataStore.getCharacter.mockResolvedValue(null);
            const controller = new quality_control_js_1.QualityController(mockVlm);
            await (0, vitest_1.expect)(controller.checkQuality('http://img.test/1.jpg', 'missing', 'img-1', 'task-1')).rejects.toThrow('Character not found: missing');
        });
    });
    (0, vitest_1.describe)('checkWithRetry', () => {
        (0, vitest_1.it)('should return immediately if first check passes', async () => {
            const report = makeReport({ passed: true, totalScore: 8.5 });
            mockAnalyzeImage.mockResolvedValue(report);
            const regenerateFn = vitest_1.vi.fn();
            const controller = new quality_control_js_1.QualityController(mockVlm);
            const result = await controller.checkWithRetry('http://img.test/1.jpg', 'char-1', 'img-1', 'task-1', regenerateFn);
            (0, vitest_1.expect)(result.passed).toBe(true);
            (0, vitest_1.expect)(regenerateFn).not.toHaveBeenCalled();
            (0, vitest_1.expect)(mockAnalyzeImage).toHaveBeenCalledTimes(1);
        });
        (0, vitest_1.it)('should retry with regeneration when below threshold', async () => {
            const failReport = makeReport({ passed: false, totalScore: 5.0, feedback: 'Hair color mismatch' });
            const passReport = makeReport({ passed: true, totalScore: 8.0 });
            mockAnalyzeImage.mockResolvedValueOnce(failReport).mockResolvedValueOnce(passReport);
            const regenerateFn = vitest_1.vi.fn().mockResolvedValue('http://img.test/2.jpg');
            const controller = new quality_control_js_1.QualityController(mockVlm);
            const result = await controller.checkWithRetry('http://img.test/1.jpg', 'char-1', 'img-1', 'task-1', regenerateFn);
            (0, vitest_1.expect)(result.passed).toBe(true);
            (0, vitest_1.expect)(regenerateFn).toHaveBeenCalledWith('Hair color mismatch');
            (0, vitest_1.expect)(mockAnalyzeImage).toHaveBeenCalledTimes(2);
        });
        (0, vitest_1.it)('should mark for manual review after max attempts', async () => {
            const failReport = makeReport({ passed: false, totalScore: 4.0, feedback: 'Poor quality' });
            mockAnalyzeImage.mockResolvedValue(failReport);
            const regenerateFn = vitest_1.vi.fn().mockResolvedValue('http://img.test/retry.jpg');
            const controller = new quality_control_js_1.QualityController(mockVlm);
            const result = await controller.checkWithRetry('http://img.test/1.jpg', 'char-1', 'img-1', 'task-1', regenerateFn, 7.0, 3);
            (0, vitest_1.expect)(result.passed).toBe(false);
            (0, vitest_1.expect)(result.feedback).toContain('[MANUAL_REVIEW_REQUIRED]');
            (0, vitest_1.expect)(regenerateFn).toHaveBeenCalledTimes(2); // 3 attempts = 2 regenerations
            (0, vitest_1.expect)(mockAnalyzeImage).toHaveBeenCalledTimes(3);
        });
        (0, vitest_1.it)('should respect custom maxAttempts', async () => {
            const failReport = makeReport({ passed: false, totalScore: 3.0, feedback: 'Bad' });
            mockAnalyzeImage.mockResolvedValue(failReport);
            const regenerateFn = vitest_1.vi.fn().mockResolvedValue('http://img.test/retry.jpg');
            const controller = new quality_control_js_1.QualityController(mockVlm);
            const result = await controller.checkWithRetry('http://img.test/1.jpg', 'char-1', 'img-1', 'task-1', regenerateFn, 7.0, 2);
            (0, vitest_1.expect)(result.passed).toBe(false);
            (0, vitest_1.expect)(result.feedback).toContain('[MANUAL_REVIEW_REQUIRED]');
            (0, vitest_1.expect)(regenerateFn).toHaveBeenCalledTimes(1); // 2 attempts = 1 regeneration
            (0, vitest_1.expect)(mockAnalyzeImage).toHaveBeenCalledTimes(2);
        });
        (0, vitest_1.it)('should pass new URL from regenerateFn to subsequent checks', async () => {
            const fail1 = makeReport({ passed: false, totalScore: 5.0, feedback: 'Fix hair' });
            const fail2 = makeReport({ passed: false, totalScore: 6.0, feedback: 'Fix skin' });
            const pass = makeReport({ passed: true, totalScore: 8.0 });
            mockAnalyzeImage
                .mockResolvedValueOnce(fail1)
                .mockResolvedValueOnce(fail2)
                .mockResolvedValueOnce(pass);
            const regenerateFn = vitest_1.vi.fn()
                .mockResolvedValueOnce('http://img.test/v2.jpg')
                .mockResolvedValueOnce('http://img.test/v3.jpg');
            const controller = new quality_control_js_1.QualityController(mockVlm);
            const result = await controller.checkWithRetry('http://img.test/v1.jpg', 'char-1', 'img-1', 'task-1', regenerateFn);
            (0, vitest_1.expect)(result.passed).toBe(true);
            // First call uses original URL
            (0, vitest_1.expect)(mockAnalyzeImage.mock.calls[0][0]).toBe('http://img.test/v1.jpg');
            // Second call uses regenerated URL
            (0, vitest_1.expect)(mockAnalyzeImage.mock.calls[1][0]).toBe('http://img.test/v2.jpg');
            // Third call uses second regenerated URL
            (0, vitest_1.expect)(mockAnalyzeImage.mock.calls[2][0]).toBe('http://img.test/v3.jpg');
        });
    });
});
//# sourceMappingURL=quality-control.test.js.map