import dotenv from 'dotenv';
import * as fs from 'fs/promises';
import * as path from 'path';
import { logger } from '../lib/logger';
import { getPinUrlFromPinterest, downloadImageFromPin } from '../lib/pinterest';
import { convertImage, extractCloth } from '../lib/image-converter';
dotenv.config();

const KEYWORDS = [
    'teen skirt outfit',
    'teen casual cute outfit',
    'cute outfit',
    'summer dress',
    'elegant dress',
    'bohemian dress',
    'ball gown dress',
    'cosplay outfit',
    'vintage skirt outfit',
    'casual skirt outfit',
    'harajuku outfit',
    'pop colorful outfit',
    'cute colorful outfit',
    'japanese idol outfit',
    'folk dress outfit',
];
const TARGET_COUNT = Number(process.env.IMAGE_COUNT || 20);
const PROMPT =
    `Change pose of the person in the image1 to stantind in front of camera, with a smile, full body visible, wearing a fashionable outfit suitable for a casual day out. The background should be a white. Ensure the lighting is bright and natural, highlighting the details of the outfit and the person's features. 
    `;

const PROMPT2 =
    `Extract clothes from image1, put it on the light gray backgroud
    `;

type ServerCfg = { baseUrl: string; outputDir: string; inputDir: string };

function getServerConfig(): ServerCfg {
    const candidates = [
        //{ baseUrl: process.env.SERVER1_COMFY_BASE_URL, outputDir: process.env.SERVER1_COMFY_OUTPUT_DIR },
        { baseUrl: process.env.SERVER2_COMFY_BASE_URL, outputDir: process.env.SERVER2_COMFY_OUTPUT_DIR },
    ].filter((s): s is { baseUrl: string; outputDir: string } => !!s.baseUrl && !!s.outputDir);

    if (candidates.length === 0) {
        throw new Error(
            'No ComfyUI server configured. Please set SERVER1_COMFY_BASE_URL/OUTPUT_DIR or SERVER2_COMFY_BASE_URL/OUTPUT_DIR in .env'
        );
    }
    const chosen = candidates[0];
    const inputDir = chosen.outputDir.replace('output', 'input');
    return { ...chosen, inputDir };
}

function sleep(ms: number) {
    return new Promise((res) => setTimeout(res, ms));
}

async function ensureDir(p: string) {
    try {
        await fs.mkdir(p, { recursive: true });
    } catch {
        // ignore
    }
}

async function collectImages(keyword: string, total: number): Promise<string[]> {
    const results: string[] = [];
    // ensure local download dir exists (pinterest.ts also ensures it, but harmless here)
    await ensureDir(path.join(process.cwd(), 'download'));

    while (results.length < total) {
        try {
            const pinUrl = await getPinUrlFromPinterest(keyword);
            if (!pinUrl) {
                logger.warn('No pin URL found, retrying...');
                await sleep(1500);
                continue;
            }
            const remaining = total - results.length;
            // attempt to grab up to 5 per pin to reduce churn
            const batchTarget = Math.min(5, remaining);
            const imgs = await downloadImageFromPin(pinUrl, batchTarget);
            if (imgs && imgs.length > 0) {
                results.push(...imgs);
                logger.info(`Downloaded ${imgs.length} image(s) from ${pinUrl}.Progress: ${results.length}/${total}`);
            } else {
                logger.warn(`Pin yielded no downloadable images: ${pinUrl}`);
            }
            await sleep(1000 + Math.random() * 1000);
        } catch (err) {
            logger.error('Error while collecting images:', err);
            await sleep(2000);
        }
    }
    return results.slice(0, total);
}

async function processImages(imagePaths: string[], server: ServerCfg) {
    await ensureDir(server.inputDir);

    for (const localImagePath of imagePaths) {
        const baseName = path.basename(localImagePath);
        const serverInputPath = path.join(server.inputDir, baseName);

        try {
            // copy source image into ComfyUI input dir so workflow node can find it by filename
            await fs.copyFile(localImagePath, serverInputPath);
            logger.info(`Copied ${localImagePath} -> ${serverInputPath}`);

            // Run conversion (sequential to avoid output race conditions)
            const generatedPath1 = await extractCloth(
                PROMPT2,
                baseName,
                server.baseUrl,
                server.outputDir,
                { width: 720, height: 1280 } // portrait
            );
            /*
                        await fs.copyFile(generatedPath1, serverInputPath);
                        const baseName2 = path.basename(localImagePath);
            
                        const generatedPath2 = await extractCloth(
                            PROMPT2,
                            baseName2,
                            server.baseUrl,
                            server.outputDir,
                            { width: 720, height: 1280 } // portrait
                        ); 
            */
            logger.info(`Generated image: ${generatedPath1}`);
        } catch (err) {
            logger.error(`Failed to convert ${localImagePath}:`, err);
        } finally {
            // cleanup both server input copy and local downloaded file
            try {
                await fs.unlink(serverInputPath);
            } catch { }
            try {
                await fs.unlink(localImagePath);
            } catch { }
            await sleep(500);
        }
    }
}

async function main() {
    const server = getServerConfig();

    // Infinite loop as requested
    while (true) {

        for (const KEYWORD of KEYWORDS) {
            logger.info(`Starting Pinterest image conversion loop for keyword: "${KEYWORD}" (target ${TARGET_COUNT})`);
            try {
                const images = await collectImages(KEYWORD, TARGET_COUNT);
                logger.info(`Collected ${images.length} image(s). Starting conversion...`);
                await processImages(images, server);
                logger.info('Iteration completed. Sleeping before next cycle...');
            } catch (err) {
                logger.error('Iteration failed:', err);
            }

            // brief pause between iterations
            await sleep(10000);
        }

    }
}

process.on('unhandledRejection', (reason) => {
    logger.error('UnhandledRejection:', reason);
});

process.on('uncaughtException', (err) => {
    logger.error('UncaughtException:', err);
});

main().catch((e) => {
    logger.error('Fatal error:', e);
});
