import fs from 'fs';
import path from 'path';
import { query } from './lib/mysql';
import { logger } from './lib/logger';
import { callLMStudio } from './lib/lmstudio';

async function main() {
    await updatePromptsFromDB();
    process.exit();
}

/**
 * Find DB records whose video_prompt contains 'cut' or 'zoom' (case-insensitive),
 * regenerate the video_prompt using LMStudio, and update the record.
 *
 * If the newly generated prompt still contains any banned words/phrases, regenerate
 * again (up to maxAttempts). If after attempts the prompt is still invalid, skip update.
 */
async function updatePromptsFromDB() {
    logger.info("Starting DB sweep for video_prompt containing 'cut' or 'zoom'...");

    // Banned regex per requirement
    const banned = /\b(cut|cuts|cutting|quick cut|insert|macro insert|close-?up|extreme close-?up|zoom|zooming|push-?in|pull-?out|whip|switch angle|change angle|montage|cross-?cut|smash cut|transition|meanwhile|later)\b/i;

    let rows: any[] = [];
    try {
        // Case-insensitive search for 'cut' or 'zoom' anywhere in video_prompt
        rows = (await query(
            "SELECT id, genre, sub_genre, scene, action, camera, video_prompt FROM video WHERE LOWER(COALESCE(video_prompt,'')) LIKE ? OR LOWER(COALESCE(video_prompt,'')) LIKE ?",
            ['%cut%', '%zoom%']
        )) as any[];
    } catch (err) {
        logger.error('DB query failed while searching for problematic prompts:', err);
        return;
    }

    if (!rows || rows.length === 0) {
        logger.info("No records found with 'cut' or 'zoom' in video_prompt.");
        return;
    }

    logger.info(`Found ${rows.length} record(s) to process.`);

    for (const row of rows) {
        const id = row.id;
        const genre = row.genre || '';
        const subGenre = row.sub_genre || '';
        const scene = row.scene || '';
        const action = row.action || '';
        const camera = row.camera || '';

        if (!genre || !subGenre || !scene) {
            logger.info(`Skipping id=${id} due to missing identification fields: genre='${genre}', sub_genre='${subGenre}', scene='${scene}'`);
            continue;
        }

        // Build LM input (similar ruleset to previous implementation)
        const lmInput = buildLMInputFromRecord(genre, subGenre, scene, action, camera, row.video_prompt);

        let finalPrompt: string | null = null;
        const maxAttempts = 10;

        for (let attempt = 1; attempt <= maxAttempts; attempt++) {
            let lmResponse: any = null;
            try {
                lmResponse = await callLMStudio(lmInput);
            } catch (err) {
                logger.warn(`LMStudio call failed for id=${id} (attempt ${attempt}): ${err}`);
                // Retry on next loop iteration
                continue;
            }

            if (!lmResponse) {
                logger.warn(`LMStudio returned empty response for id=${id} (attempt ${attempt}).`);
                continue;
            }

            const videoPrompt = lmResponse.videoPrompt || lmResponse.video_prompt || lmResponse.prompt || null;
            if (!videoPrompt || typeof videoPrompt !== 'string') {
                logger.warn(`LMStudio did not return a valid videoPrompt for id=${id} (attempt ${attempt}).`);
                continue;
            }

            // Check banned regex
            if (banned.test(videoPrompt)) {
                logger.info(`Generated prompt for id=${id} (attempt ${attempt}) still contains banned phrases - retrying.`);
                logger.info(videoPrompt);
                // If last attempt, we will fall through and skip update
                continue;
            }

            // Passed banned check
            finalPrompt = videoPrompt;
            break;
        }

        if (!finalPrompt) {
            logger.warn(`Could not generate a clean prompt for id=${id} after ${maxAttempts} attempts. Skipping update.`);
            continue;
        }

        // Update DB
        try {
            await query('UPDATE video SET video_prompt = ? WHERE id = ?', [finalPrompt, id]);
            logger.info(`Updated video_prompt for id=${id}`);
        } catch (err) {
            logger.error(`Failed to update video_prompt for id=${id}: ${err}`);
        }
    }

    logger.info('Finished DB sweep for problematic prompts.');
}

/**
 * Helper to construct LM input for a single DB record.
 * Keeps the same HARD RULES and prohibited list as previous data-driven generation.
 */
function buildLMInputFromRecord(
    genre: string,
    subGenre: string,
    finalScene: string,
    chosenAction: string,
    camera: string,
    existingPrompt: string | undefined
) {
    const accents = 'none';
    const mood = 'n/a';
    const lighting = 'n/a';
    const style = 'n/a';

    const lmInput = `
Return exactly one JSON object: { "videoPrompt": "..." } and nothing else.

Write "videoPrompt" in 100–150 words, present tense, plain concrete language.

HARD RULES (must comply):
- One continuous shot ("one take", "oner"). Real-time 8 seconds. No edits.
- Fixed location and vantage. Do not change background or angle.
- Lens and focal length locked. No zooms, no close-ups that imply a lens change, no rack zoom.
- Camera motion: at most subtle pan/tilt/dolly within 1 meter while staying in the same spot.
- Keep framing consistent (e.g., medium-wide two-shot). No “another shot/meanwhile.”
- Describe: (1) main action, (2) framing & motion, (3) lighting & mood, (4) style & small accents.
- Use clear simple sentences. No metaphors or poetic language.

PROHIBITED WORDS/PHRASES (case-insensitive): 
cut, cuts, cutting, quick cut, insert, macro insert, close-up, extreme close-up,
zoom, zooms, zooming, push-in, pull-out, whip, switch angle, change angle,
montage, cross-cut, smash cut, transition, meanwhile, later.

If proximity is needed, say: "the camera glides slightly closer while staying in the same position."

Here is information of the scene, please generate prompt for the video based on these information for key "videoPrompt":
Genre: ${genre}
Sub-Genre: ${subGenre}
Scene: ${finalScene}
Action: ${chosenAction || 'n/a'}
Camera: ${camera || 'static or subtle movement (stay within scene)'}
Accents: ${accents}
Mood: ${mood}
Lighting: ${lighting}
Style: ${style}
`;

    return lmInput;
}

main();
