"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ImageEngine = void 0;
const uuid_1 = require("uuid");
const path_1 = require("path");
const promises_1 = require("fs/promises");
const logger_js_1 = require("../../utils/logger.js");
const config_js_1 = require("../../utils/config.js");
const index_js_1 = require("../queue-system/index.js");
const index_js_2 = require("../data-store/index.js");
const prompt_builder_js_1 = require("../character-engine/prompt-builder.js");
const kie_client_js_1 = require("./kie-client.js");
const templates_js_1 = require("./templates.js");
const log = (0, logger_js_1.createModuleLogger)('image-engine');
const MAX_RETRIES = 3;
class ImageEngine {
    kieClient;
    templateManager;
    /** Tracks retry attempts per original generation request */
    retryMap = new Map();
    constructor(kieClient, templateManager) {
        this.kieClient = kieClient || (0, kie_client_js_1.createKIEClient)();
        this.templateManager = templateManager || new templates_js_1.TemplateManager();
    }
    /**
     * Combines Scene Template + Character DNA + Master JSON Prompt into a final prompt string.
     */
    buildFinalPrompt(dna, masterPrompt, template) {
        const subject = (0, prompt_builder_js_1.buildSubjectDescription)(dna);
        const camera = (0, prompt_builder_js_1.buildCameraSettings)(dna);
        const clothing = masterPrompt.clothing;
        const lighting = template.environment.lighting || masterPrompt.lighting;
        const aesthetic = masterPrompt.aesthetic;
        // Fill template placeholders
        let prompt = template.promptTemplate;
        prompt = prompt.replace(/\{subject\}/g, subject);
        prompt = prompt.replace(/\{camera\}/g, camera);
        prompt = prompt.replace(/\{clothing\}/g, clothing);
        prompt = prompt.replace(/\{lighting\}/g, lighting);
        prompt = prompt.replace(/\{aesthetic\}/g, aesthetic);
        prompt = prompt.replace(/\{environment\}/g, masterPrompt.environment);
        return prompt;
    }
    /**
     * Queues an image generation task. Returns the queue task ID.
     */
    async generateImage(characterId, templateId, options) {
        const character = await index_js_2.dataStore.getCharacter(characterId);
        if (!character)
            throw new Error(`Character not found: ${characterId}`);
        const masterPrompt = await index_js_2.dataStore.getMasterPrompt(characterId);
        if (!masterPrompt)
            throw new Error(`Master prompt not found for character: ${characterId}`);
        const template = await index_js_2.dataStore.getTemplate(templateId);
        if (!template)
            throw new Error(`Template not found: ${templateId}`);
        const finalPrompt = this.buildFinalPrompt(character, masterPrompt, template);
        const model = options?.model || 'flux-2';
        const aspectRatio = options?.aspectRatio || template.defaultAspectRatio || '1:1';
        const queueItem = await index_js_1.queue.add('image_generation', characterId, {
            prompt: finalPrompt,
            negativePrompt: masterPrompt.negative_prompt,
            model,
            aspectRatio,
            templateId,
            webhookUrl: config_js_1.config.webhookBaseUrl ? `${config_js_1.config.webhookBaseUrl}/api/v1/webhooks/kie` : undefined,
            isReferenceImage: false,
        });
        // Track for retry
        this.retryMap.set(queueItem.id, { attempts: 0, characterId, templateId, options });
        log.info({ taskId: queueItem.id, characterId, templateId, model }, 'Image generation queued');
        return queueItem.id;
    }
    /**
     * Processes a completed image result from webhook callback.
     * Downloads and saves the image to the character's generated-images directory.
     */
    async handleWebhookResult(taskId, resultUrl) {
        log.info({ taskId, resultUrl }, 'Processing webhook result');
        const queueItem = index_js_1.queue.getById(taskId);
        if (!queueItem) {
            log.warn({ taskId }, 'Queue item not found for webhook result');
            return;
        }
        const characterId = queueItem.characterId;
        const imageId = (0, uuid_1.v4)();
        const imagesDir = index_js_2.dataStore.getGeneratedImagesDir(characterId);
        const imagePath = (0, path_1.resolve)(imagesDir, `${imageId}.jpg`);
        try {
            const response = await fetch(resultUrl);
            if (!response.ok)
                throw new Error(`Failed to download image: ${response.status}`);
            const buffer = Buffer.from(await response.arrayBuffer());
            await (0, promises_1.writeFile)(imagePath, buffer);
            await index_js_1.queue.updateStatus(taskId, 'completed');
            log.info({ taskId, imageId, characterId }, 'Image saved successfully');
        }
        catch (err) {
            log.error({ taskId, err: err.message }, 'Failed to process webhook result');
            await this.handleFailure(taskId, err.message);
        }
    }
    /**
     * Handles a failed image generation task. Auto-retries up to MAX_RETRIES.
     */
    async handleFailure(taskId, error) {
        const retryInfo = this.retryMap.get(taskId);
        if (retryInfo && retryInfo.attempts < MAX_RETRIES) {
            retryInfo.attempts++;
            log.info({ taskId, attempt: retryInfo.attempts, maxRetries: MAX_RETRIES }, 'Retrying image generation');
            try {
                const newTaskId = await this.generateImage(retryInfo.characterId, retryInfo.templateId, retryInfo.options);
                // Transfer retry tracking to new task
                const newRetryInfo = this.retryMap.get(newTaskId);
                if (newRetryInfo) {
                    newRetryInfo.attempts = retryInfo.attempts;
                }
            }
            catch (retryErr) {
                log.error({ taskId, err: retryErr.message }, 'Retry failed');
            }
            this.retryMap.delete(taskId);
        }
        else {
            log.error({ taskId, error }, 'Image generation failed after max retries');
            await index_js_1.queue.updateStatus(taskId, 'failed', error);
            this.retryMap.delete(taskId);
        }
    }
}
exports.ImageEngine = ImageEngine;
//# sourceMappingURL=index.js.map