T.ME/BIBIL_0DAY
CasperSecurity


Server : Apache/2
System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64
User : gositeme ( 1004)
PHP Version : 8.2.29
Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Directory :  /home/gositeme/domains/soundstudiopro.com/private_html/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/soundstudiopro.com/private_html/create_music.php
<?php
// Set execution time limit and memory limit
set_time_limit(60); // 60 seconds max execution time
ini_set('max_execution_time', 60);
ini_set('memory_limit', '256M');

// Enable error reporting for debugging (remove in production)
error_reporting(E_ALL);
ini_set('display_errors', 0); // Don't display errors to users, but log them
ini_set('log_errors', 1);

// Start output buffering to ensure headers can be sent
ob_start();

// Set error handler to catch fatal errors and log detailed information
register_shutdown_function(function() {
    $error = error_get_last();
    if ($error !== NULL && in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE, E_RECOVERABLE_ERROR])) {
        $error_details = "FATAL ERROR in create_music.php:\n";
        $error_details .= "Type: " . $error['type'] . "\n";
        $error_details .= "Message: " . $error['message'] . "\n";
        $error_details .= "File: " . $error['file'] . "\n";
        $error_details .= "Line: " . $error['line'] . "\n";
        $error_details .= "POST data: " . print_r($_POST, true) . "\n";
        error_log($error_details);
        
        if (session_status() === PHP_SESSION_ACTIVE && !headers_sent()) {
            try {
                $_SESSION['error'] = 'A server error occurred: ' . htmlspecialchars($error['message']) . ' (Line ' . $error['line'] . ')';
                header('Location: index.php#create');
                exit;
            } catch (Exception $e) {
                error_log("Error in shutdown function: " . $e->getMessage());
            }
        }
    }
});

session_start();

// Include translation system
require_once __DIR__ . '/includes/translations.php';

// Log that script started - use both error_log and file_put_contents for immediate feedback
$log_msg = "[" . date('Y-m-d H:i:s') . "] create_music.php: Script started, POST method: " . $_SERVER['REQUEST_METHOD'] . ", User ID: " . ($_SESSION['user_id'] ?? 'N/A') . "\n";
error_log($log_msg);
file_put_contents(__DIR__ . '/create_music_debug.log', $log_msg, FILE_APPEND);

// Check if user is logged in
if (!isset($_SESSION['user_id'])) {
    error_log("create_music.php: User not logged in, redirecting to login");
    header('Location: auth/login.php');
    exit;
}

error_log("create_music.php: User logged in, user_id: " . $_SESSION['user_id']);

// Check if form was submitted
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    header('Location: index.php#create');
    exit;
}

// SECURITY: CSRF Protection
require_once 'includes/security.php';
$csrf_token = $_POST['csrf_token'] ?? '';
if (!validateCSRFToken($csrf_token)) {
    error_log("SECURITY: CSRF token validation failed in create_music.php from IP: " . ($_SERVER['REMOTE_ADDR'] ?? 'unknown'));
    $_SESSION['error'] = 'Security validation failed. Please refresh the page and try again.';
    header('Location: index.php#create');
    exit;
}

error_log("create_music.php: Loading database config");
require_once 'config/database.php';

error_log("create_music.php: Getting database connection");
try {
    $pdo = getDBConnection();
    if (!$pdo) {
        throw new Exception('Database connection failed - getDBConnection returned null/false');
    }
    error_log("create_music.php: Database connection successful");
} catch (Exception $e) {
    error_log("Database connection error: " . $e->getMessage());
    error_log("Database connection error trace: " . $e->getTraceAsString());
    $_SESSION['error'] = 'Database connection error. Please try again.';
    ob_end_clean();
    header('Location: index.php#create');
    exit;
} catch (Error $e) {
    error_log("Database connection fatal error: " . $e->getMessage());
    error_log("Database connection fatal error trace: " . $e->getTraceAsString());
    $_SESSION['error'] = 'Database connection fatal error: ' . htmlspecialchars($e->getMessage());
    ob_end_clean();
    header('Location: index.php#create');
    exit;
}

// Ensure UTF-8 encoding for all input (only if mbstring extension is available)
if (function_exists('mb_internal_encoding')) {
    mb_internal_encoding('UTF-8');
    if (function_exists('mb_http_output')) {
        mb_http_output('UTF-8');
    }
    // Note: mb_http_input() is used to detect input encoding, not set it
    // It requires a type parameter: "G", "P", "C", "S", "I", or "L"
    // We don't need to call it here as we handle encoding manually
}

// Debug: Log all POST data (with compatibility check for JSON flags)
$json_post_flags = 0;
if (defined('JSON_UNESCAPED_UNICODE')) {
    $json_post_flags |= JSON_UNESCAPED_UNICODE;
}
if (defined('JSON_UNESCAPED_SLASHES')) {
    $json_post_flags |= JSON_UNESCAPED_SLASHES;
}
error_log("POST data received: " . json_encode($_POST, $json_post_flags));

// Get form data - preserve special characters
$type = isset($_POST['type']) ? trim($_POST['type']) : 'music';
$prompt = isset($_POST['prompt']) ? $_POST['prompt'] : '';
$advancedPrompt = isset($_POST['advancedPrompt']) ? trim($_POST['advancedPrompt']) : '';
// Get title - preserve it even if empty (will be used for database)
$title = isset($_POST['title']) ? trim($_POST['title']) : '';
error_log("create_music.php: Title from POST: '$title' (length: " . strlen($title) . ")");
// Default to V5 (latest model) - supports up to 8 minutes
// V3_5/V4 only support 4 minutes max
$model_name = isset($_POST['model_name']) ? trim($_POST['model_name']) : 'V5';
// Duration will be parsed later by parseDuration() function, but get raw value first
$duration = $_POST['duration'] ?? '360';
// Custom Mode is always false now (removed from UI to simplify)
$customMode = 'false';

// Get advanced form fields
$genre = $_POST['genre'] ?? '';
$key = $_POST['key'] ?? '';
$tempo = $_POST['tempo'] ?? '';
$mood = $_POST['mood'] ?? '';
$instrumental = $_POST['instrumental'] ?? 'false';
error_log("create_music.php: Instrumental from POST: '$instrumental' (type: " . gettype($instrumental) . ")");
$energy = $_POST['energy'] ?? '';
$excitement = $_POST['excitement'] ?? '';
$scale = $_POST['scale'] ?? '';
$tags = $_POST['tags'] ?? '';

// Get new additional fields
$timeSignature = $_POST['timeSignature'] ?? '';
$language = $_POST['language'] ?? '';
$voiceType = $_POST['voiceType'] ?? '';
$useCase = $_POST['useCase'] ?? '';
$instruments = $_POST['instruments'] ?? '';

// Get Pro Mode fields
$proTitle = $_POST['proTitle'] ?? '';
$proModel = $_POST['proModel'] ?? '';
$proVariations = $_POST['proVariations'] ?? '';
$proKey = $_POST['proKey'] ?? '';
$proScale = $_POST['proScale'] ?? '';
$proTimeSignature = $_POST['proTimeSignature'] ?? '';
$proTempo = $_POST['proTempo'] ?? '';
$proChordProgression = $_POST['proChordProgression'] ?? '';
$proOctave = $_POST['proOctave'] ?? '';
$proGenre = $_POST['proGenre'] ?? '';
$proSubGenre = $_POST['proSubGenre'] ?? '';
$proDecade = $_POST['proDecade'] ?? '';
$proLeadInstrument = $_POST['proLeadInstrument'] ?? '';
$proRhythmSection = $_POST['proRhythmSection'] ?? '';
$proHarmonySection = $_POST['proHarmonySection'] ?? '';
$proArrangement = $_POST['proArrangement'] ?? '';
$proComplexity = $_POST['proComplexity'] ?? '';
$proDensity = $_POST['proDensity'] ?? '';
$proReverb = $_POST['proReverb'] ?? '';
$proCompression = $_POST['proCompression'] ?? '';
$proStereoWidth = $_POST['proStereoWidth'] ?? '';
$proBassLevel = $_POST['proBassLevel'] ?? '';
$proMidLevel = $_POST['proMidLevel'] ?? '';
$proTrebleLevel = $_POST['proTrebleLevel'] ?? '';
$proVoiceType = $_POST['proVoiceType'] ?? '';
$proLanguage = $_POST['proLanguage'] ?? '';
$proVocalStyle = $_POST['proVocalStyle'] ?? '';
$proLyricTheme = $_POST['proLyricTheme'] ?? '';
$proRhymeScheme = $_POST['proRhymeScheme'] ?? '';
$proHookFrequency = $_POST['proHookFrequency'] ?? '';
$proIntroLength = $_POST['proIntroLength'] ?? '';
$proVerseChorusRatio = $_POST['proVerseChorusRatio'] ?? '';
$proBridge = $_POST['proBridge'] ?? '';
$proOutroStyle = $_POST['proOutroStyle'] ?? '';
$proBuildUps = $_POST['proBuildUps'] ?? '';
$proTransitions = $_POST['proTransitions'] ?? '';
$proDuration = $_POST['proDuration'] ?? '';
$proQuality = $_POST['proQuality'] ?? '';
$proMood = $_POST['proMood'] ?? '';
$proEnergy = $_POST['proEnergy'] ?? '';
$proExcitement = $_POST['proExcitement'] ?? '';
$proDanceability = $_POST['proDanceability'] ?? '';
$proPrompt = $_POST['proPrompt'] ?? '';
$proTags = $_POST['proTags'] ?? '';

// Enable customMode when pro mode is used (pro mode allows up to 5000 characters)
// Pro mode is detected when proPrompt is provided (and not just whitespace)
if (!empty($proPrompt) && trim($proPrompt) !== '') {
    $customMode = 'true';
    error_log("Pro mode detected (proPrompt provided), enabling customMode to allow up to 5000 characters");
}

// Debug: Log individual fields (truncate long prompts for logging)
if (function_exists('mb_strlen')) {
    $promptLen = mb_strlen($prompt);
    $advancedPromptLen = mb_strlen($advancedPrompt);
    $proPromptLen = mb_strlen($proPrompt);
} else {
    $promptLen = strlen($prompt);
    $advancedPromptLen = strlen($advancedPrompt);
    $proPromptLen = strlen($proPrompt);
}

error_log("Form fields - type: $type, prompt length: $promptLen, advancedPrompt length: $advancedPromptLen, proPrompt length: $proPromptLen, title: '$title', model_name: '$model_name', duration: $duration, proDuration: " . ($proDuration ?? 'not set') . ", customMode: $customMode, proVoiceType: '$proVoiceType'");

// Use pro prompt if available, then advanced prompt, otherwise simple prompt
// Preserve all special characters - only convert encoding if actually needed
$finalPrompt = !empty($proPrompt) ? $proPrompt : (!empty($advancedPrompt) ? $advancedPrompt : $prompt);
// Only convert if it's NOT already valid UTF-8 (don't re-encode valid UTF-8 as it can corrupt the prompt)
if (function_exists('mb_check_encoding') && function_exists('mb_convert_encoding')) {
    if (!mb_check_encoding($finalPrompt, 'UTF-8')) {
        $finalPrompt = mb_convert_encoding($finalPrompt, 'UTF-8', 'auto');
    }
}
// Decode any HTML entities that might have been encoded (like &#039; or &apos;)
$finalPrompt = html_entity_decode($finalPrompt, ENT_QUOTES | ENT_HTML5, 'UTF-8');
$finalPrompt = trim($finalPrompt);

// Handle common user mistakes: Remove "./." pattern if it appears at the start of the prompt
// This pattern is sometimes mistakenly used as a command prefix but is not needed
if (preg_match('/^\.\/\.\s*/', $finalPrompt)) {
    $originalPrompt = $finalPrompt;
    $finalPrompt = preg_replace('/^\.\/\.\s*/', '', $finalPrompt);
    $finalPrompt = trim($finalPrompt);
    error_log("WARNING: Removed './.' prefix from prompt. Original: '" . substr($originalPrompt, 0, 100) . "', Cleaned: '" . substr($finalPrompt, 0, 100) . "'");
}

// Parse structured metadata from prompt (BPM, Key, Style, Vocal, Mood, Sound, Duration)
// Format: "BPM: 99 | Key: C Minor (432 Hz) | Camelot: 5A | Style: ... | Vocal: ... | Mood: ... | Sound: ... | Duration: 5:32"
$extractedMetadata = [
    'bpm' => null,
    'key' => null,
    'style' => null,
    'vocal' => null,
    'mood' => null,
    'sound' => null,
    'duration' => null
];

// Extract BPM
if (preg_match('/bpm[:\s]+(\d+)/i', $finalPrompt, $matches)) {
    $extractedMetadata['bpm'] = intval($matches[1]);
    error_log("Extracted BPM from prompt: " . $extractedMetadata['bpm']);
}

// Extract Key (e.g., "Key: C Minor (432 Hz)" -> "C Minor")
if (preg_match('/key[:\s]+([^|(]+?)(?:\s*\([^)]+\))?/i', $finalPrompt, $matches)) {
    $extractedMetadata['key'] = trim($matches[1]);
    error_log("Extracted Key from prompt: " . $extractedMetadata['key']);
}

// Extract Style (already handled later, but store here for reference)
if (preg_match('/style[:\s]+([^|]+?)(?:\s*[:\-|]|$)/i', $finalPrompt, $matches)) {
    $extractedMetadata['style'] = trim($matches[1]);
    error_log("Extracted Style from prompt: " . $extractedMetadata['style']);
}

// Extract Vocal description
if (preg_match('/vocal[:\s]+([^|]+?)(?:\s*[:\-|]|$)/i', $finalPrompt, $matches)) {
    $extractedMetadata['vocal'] = trim($matches[1]);
    error_log("Extracted Vocal from prompt: " . $extractedMetadata['vocal']);
}

// Extract Mood
if (preg_match('/mood[:\s]+([^|]+?)(?:\s*[:\-|]|$)/i', $finalPrompt, $matches)) {
    $extractedMetadata['mood'] = trim($matches[1]);
    error_log("Extracted Mood from prompt: " . $extractedMetadata['mood']);
}

// Extract Sound description
if (preg_match('/sound[:\s]+([^|]+?)(?:\s*[:\-|]|$)/i', $finalPrompt, $matches)) {
    $extractedMetadata['sound'] = trim($matches[1]);
    error_log("Extracted Sound from prompt: " . $extractedMetadata['sound']);
}

// Extract Duration from structured format (e.g., "Duration: 5:32")
if (preg_match('/duration[:\s]+([^|]+?)(?:\s*[:\-|]|$)/i', $finalPrompt, $matches)) {
    $durationText = trim($matches[1]);
    // Parse duration format like "5:32" (5 minutes 32 seconds)
    if (preg_match('/^(\d+):(\d+)$/', $durationText, $durMatches)) {
        $extractedMetadata['duration'] = (intval($durMatches[1]) * 60) + intval($durMatches[2]);
        error_log("Extracted Duration from structured format: $durationText = " . $extractedMetadata['duration'] . " seconds");
    } elseif (is_numeric($durationText)) {
        $extractedMetadata['duration'] = intval($durationText);
        error_log("Extracted Duration from structured format: " . $extractedMetadata['duration'] . " seconds");
    }
}

// Also extract duration from natural language in the prompt (e.g., "5-6 minutes", "5 to 6 min", "about 5 minutes")
// Only do this if we haven't already extracted from structured format
if (empty($extractedMetadata['duration'])) {
    // Look for patterns like "5-6 minutes", "5 to 6 minutes", "5-6 min", "5-6mins", "about 5 minutes", "5 minutes"
    // Also handle ranges like "5-7 minutes" -> use the higher end (7 minutes = 420 seconds)
    if (preg_match('/(\d+)\s*[-–—to]\s*(\d+)\s*(?:minutes?|mins?|min)/i', $finalPrompt, $rangeMatches)) {
        $minMinutes = intval($rangeMatches[1]);
        $maxMinutes = intval($rangeMatches[2]);
        // Use the higher end of the range to ensure we get closer to requested length
        $extractedMetadata['duration'] = $maxMinutes * 60;
        error_log("Extracted Duration from natural language range: {$minMinutes}-{$maxMinutes} minutes, using {$maxMinutes} minutes = " . $extractedMetadata['duration'] . " seconds");
    } elseif (preg_match('/(?:about|around|approximately|roughly)?\s*(\d+)\s*(?:minutes?|mins?|min)/i', $finalPrompt, $singleMatches)) {
        $minutes = intval($singleMatches[1]);
        $extractedMetadata['duration'] = $minutes * 60;
        error_log("Extracted Duration from natural language: {$minutes} minutes = " . $extractedMetadata['duration'] . " seconds");
    } elseif (preg_match('/(\d+)\s*[-–—to]\s*(\d+)\s*(?:seconds?|secs?|sec)/i', $finalPrompt, $secRangeMatches)) {
        $minSeconds = intval($secRangeMatches[1]);
        $maxSeconds = intval($secRangeMatches[2]);
        // Use the higher end of the range
        $extractedMetadata['duration'] = $maxSeconds;
        error_log("Extracted Duration from natural language range: {$minSeconds}-{$maxSeconds} seconds, using {$maxSeconds} seconds");
    } elseif (preg_match('/(?:about|around|approximately|roughly)?\s*(\d+)\s*(?:seconds?|secs?|sec)/i', $finalPrompt, $secSingleMatches)) {
        $seconds = intval($secSingleMatches[1]);
        $extractedMetadata['duration'] = $seconds;
        error_log("Extracted Duration from natural language: {$seconds} seconds");
    }
}

// CRITICAL: If customMode is false, skip ALL enhancement and keep original prompt format
// This matches the successful API request format that had the full structured prompt
$isNonCustomMode = ($customMode === 'false' || $customMode === false);
if ($isNonCustomMode) {
    // Non-custom mode: Keep original prompt format with all structured metadata intact
    // This matches successful requests that had: "OM AH HUM (Root Awakening)\r\n\r\nBPM: 99\r\n\r\nKey: C Minor..."
    // Use the original prompt before any enhancement/cleaning - restore from the raw input
    $finalPrompt = !empty($proPrompt) ? $proPrompt : (!empty($advancedPrompt) ? $advancedPrompt : $prompt);
    // Only decode HTML entities, don't enhance or clean - keep the structured format
    $finalPrompt = html_entity_decode($finalPrompt, ENT_QUOTES | ENT_HTML5, 'UTF-8');
    $finalPrompt = trim($finalPrompt);
    error_log("Non-custom mode: Keeping original structured prompt format (not enhancing). Length: " . strlen($finalPrompt));
} else {
    // Custom mode: Enhance the prompt by incorporating extracted metadata into natural language
    // This helps the API understand the full context better
    $enhancedPromptParts = [];

    // Start with the original prompt (but remove the structured metadata lines to avoid duplication)
    $cleanPrompt = $finalPrompt;
    // Remove structured metadata lines (BPM, Key, Camelot, Style, Vocal, Mood, Sound, Duration)
    // BUT: Only remove if there's other content, otherwise keep the structured format
    $hasOtherContent = false;
    $testClean = preg_replace('/\s*(bpm|key|camelot|style|vocal|mood|sound|duration)[:\s]+[^|\n]+(?:\s*[:\-|]|$)/i', '', $cleanPrompt);
    $testClean = preg_replace('/\s*\|\s*/', ' ', $testClean);
    $testClean = trim($testClean);
    if (!empty($testClean)) {
        $hasOtherContent = true;
        $cleanPrompt = $testClean;
    } else {
        // If removing metadata leaves nothing, keep the original structured format
        // This handles cases where user ONLY provides structured metadata
        $cleanPrompt = $finalPrompt;
    }

    $cleanPrompt = trim($cleanPrompt);

    if (!empty($cleanPrompt)) {
        $enhancedPromptParts[] = $cleanPrompt;
    }

    // Add Vocal description if available
    if (!empty($extractedMetadata['vocal'])) {
        $enhancedPromptParts[] = "Vocal style: " . $extractedMetadata['vocal'];
    }

    // Add Sound description if available
    if (!empty($extractedMetadata['sound'])) {
        $enhancedPromptParts[] = "Sound design: " . $extractedMetadata['sound'];
    }

    // Add Mood if available
    if (!empty($extractedMetadata['mood'])) {
        $enhancedPromptParts[] = "Mood: " . $extractedMetadata['mood'];
    }

    // Add BPM and Key if available (helps with musical accuracy)
    if (!empty($extractedMetadata['bpm']) || !empty($extractedMetadata['key'])) {
        $tempoKeyParts = [];
        if (!empty($extractedMetadata['bpm'])) {
            $tempoKeyParts[] = $extractedMetadata['bpm'] . " BPM";
        }
        if (!empty($extractedMetadata['key'])) {
            $tempoKeyParts[] = "key of " . $extractedMetadata['key'];
        }
        if (!empty($tempoKeyParts)) {
            $enhancedPromptParts[] = "Musical parameters: " . implode(", ", $tempoKeyParts);
        }
    }

    // Combine all parts into enhanced prompt
    if (!empty($enhancedPromptParts)) {
        $finalPrompt = implode(". ", $enhancedPromptParts) . ".";
        error_log("Enhanced prompt created from structured metadata. Original length: " . strlen($cleanPrompt) . ", Enhanced length: " . strlen($finalPrompt));
    } else {
        // If no enhancement, use original (or fallback to original if cleaning removed everything)
        $finalPrompt = !empty($cleanPrompt) ? $cleanPrompt : $finalPrompt;
        // If still empty, use the original raw prompt before any processing
        if (empty($finalPrompt)) {
            $finalPrompt = !empty($proPrompt) ? $proPrompt : (!empty($advancedPrompt) ? $advancedPrompt : $prompt);
            error_log("All prompt processing resulted in empty, using original raw prompt");
        }
    }
}

// Use extracted duration if available and no duration was provided in form
// Also use it if the form duration is the default (360) and we found a better match in the prompt
if (!empty($extractedMetadata['duration'])) {
    if (empty($duration) && empty($proDuration)) {
        $duration = $extractedMetadata['duration'];
        error_log("Using extracted duration from prompt (no form value): $duration seconds");
    } elseif ($duration == '360' && empty($proDuration)) {
        // If form has default 360 but prompt specifies something else, use the prompt value
        $duration = $extractedMetadata['duration'];
        error_log("Using extracted duration from prompt (overriding default 360): $duration seconds");
    }
}

// If prompt is empty, try to use genre/style field or other fields as fallback
// This handles cases where user puts description in genre field instead of prompt field
if (empty($finalPrompt)) {
    $fallbackPrompt = '';
    
    // Try genre field first (most likely where user put their description)
    if (!empty($genre)) {
        $fallbackPrompt = $genre;
        error_log("Prompt is empty, using genre field as fallback: '$genre'");
    } elseif (!empty($proGenre)) {
        $fallbackPrompt = $proGenre;
        error_log("Prompt is empty, using proGenre field as fallback: '$proGenre'");
    } elseif (!empty($tags)) {
        $fallbackPrompt = $tags;
        error_log("Prompt is empty, using tags field as fallback: '$tags'");
    } elseif (!empty($title)) {
        $fallbackPrompt = $title;
        error_log("Prompt is empty, using title field as fallback: '$title'");
    }
    
    // If we found a fallback, use it as the prompt
    if (!empty($fallbackPrompt)) {
        $finalPrompt = $fallbackPrompt;
        error_log("Using fallback prompt: '$finalPrompt'");
        
        // Re-parse metadata from the fallback prompt since it might contain structured data
        // Extract BPM
        if (preg_match('/bpm[:\s]+(\d+)/i', $finalPrompt, $matches)) {
            $extractedMetadata['bpm'] = intval($matches[1]);
            error_log("Extracted BPM from fallback prompt: " . $extractedMetadata['bpm']);
        }
        
        // Extract Key
        if (preg_match('/key[:\s]+([^|(]+?)(?:\s*\([^)]+\))?/i', $finalPrompt, $matches)) {
            $extractedMetadata['key'] = trim($matches[1]);
            error_log("Extracted Key from fallback prompt: " . $extractedMetadata['key']);
        }
        
        // Extract Style
        if (preg_match('/style[:\s]+([^|]+?)(?:\s*[:\-|]|$)/i', $finalPrompt, $matches)) {
            $extractedMetadata['style'] = trim($matches[1]);
            error_log("Extracted Style from fallback prompt: " . $extractedMetadata['style']);
        }
        
        // Extract Duration
        if (preg_match('/duration[:\s]+([^|]+?)(?:\s*[:\-|]|$)/i', $finalPrompt, $matches)) {
            $durationText = trim($matches[1]);
            if (preg_match('/^(\d+):(\d+)$/', $durationText, $durMatches)) {
                $extractedMetadata['duration'] = (intval($durMatches[1]) * 60) + intval($durMatches[2]);
                error_log("Extracted Duration from fallback prompt: $durationText = " . $extractedMetadata['duration'] . " seconds");
            } elseif (is_numeric($durationText)) {
                $extractedMetadata['duration'] = intval($durationText);
                error_log("Extracted Duration from fallback prompt: " . $extractedMetadata['duration'] . " seconds");
            }
        }
        
        // Also check for natural language duration
        if (empty($extractedMetadata['duration'])) {
            if (preg_match('/(\d+)\s*[-–—to]\s*(\d+)\s*(?:minutes?|mins?|min)/i', $finalPrompt, $rangeMatches)) {
                $minMinutes = intval($rangeMatches[1]);
                $maxMinutes = intval($rangeMatches[2]);
                $extractedMetadata['duration'] = $maxMinutes * 60;
                error_log("Extracted Duration from fallback prompt natural language: {$minMinutes}-{$maxMinutes} minutes = " . $extractedMetadata['duration'] . " seconds");
            } elseif (preg_match('/(?:about|around|approximately|roughly)?\s*(\d+)\s*(?:minutes?|mins?|min)/i', $finalPrompt, $singleMatches)) {
                $minutes = intval($singleMatches[1]);
                $extractedMetadata['duration'] = $minutes * 60;
                error_log("Extracted Duration from fallback prompt: {$minutes} minutes = " . $extractedMetadata['duration'] . " seconds");
            }
        }
    } else {
        // No fallback available, show error
        error_log("ERROR: Final prompt is empty after processing and no fallback fields available!");
        $_SESSION['error'] = 'Please provide a music description in the "Describe your music" field.';
        ob_end_clean();
        header('Location: index.php#create');
        exit;
    }
}

// Use pro title if available, otherwise use regular title
// IMPORTANT: Always use the title if provided, even in simple mode - it should be saved to database
$finalTitle = !empty($proTitle) ? $proTitle : $title;

// If no title was provided, try to extract it from the prompt
// Look for patterns like "2. TITLE" or "NUMBER. TITLE (Subtitle)" at the start of the prompt
if (empty($finalTitle) && !empty($finalPrompt)) {
    // Match pattern: number followed by period, space, then title (possibly with parentheses)
    // Example: "2. LAM (Descent into Earth)" or "1. Song Title"
    if (preg_match('/^(\d+\.\s+)([^\n]+?)(?:\n|$)/', $finalPrompt, $matches)) {
        $extractedTitle = trim($matches[2]); // Get the title part (without the number prefix)
        // Check length using mb_strlen for proper character counting
        $extractedTitleLength = function_exists('mb_strlen') ? mb_strlen($extractedTitle, 'UTF-8') : strlen($extractedTitle);
        if (!empty($extractedTitle) && $extractedTitleLength <= 255) {
            // Truncate to 80 chars for API compatibility (even though DB allows 255)
            if ($extractedTitleLength > 80) {
                $finalTitle = function_exists('mb_substr') ? mb_substr($extractedTitle, 0, 80, 'UTF-8') : substr($extractedTitle, 0, 80);
                error_log("Extracted title exceeded 80 characters, truncated: '$finalTitle'");
            } else {
                $finalTitle = $extractedTitle;
            }
            // Remove the title line from the prompt
            $finalPrompt = preg_replace('/^' . preg_quote($matches[0], '/') . '\s*/m', '', $finalPrompt);
            $finalPrompt = trim($finalPrompt);
            error_log("create_music.php: Extracted title from prompt: '$finalTitle'");
        }
    }
}

error_log("create_music.php: finalTitle after processing: '$finalTitle' (proTitle: '$proTitle', title: '$title')");

// For advanced mode: Incorporate all advanced settings into the prompt
// CRITICAL: Only process if we're actually in Advanced Mode (advancedPrompt has content)
// This prevents Simple Mode from accidentally using Advanced Mode default values
$hasAdvancedSettings = !empty($advancedPrompt) || !empty($tempo) || !empty($key) || !empty($scale) || 
                       !empty($timeSignature) || !empty($energy) || !empty($excitement) || !empty($mood) ||
                       !empty($language) || !empty($voiceType) || !empty($useCase) || !empty($instruments);

// IMPORTANT: Only add Advanced settings if advancedPrompt is NOT empty (user is in Advanced Mode)
// This prevents Simple Mode from getting "Musical specifications" added when Advanced fields have defaults
if (!empty($advancedPrompt) && $hasAdvancedSettings) {
    // Only process advanced settings if we're in advanced mode (advancedPrompt exists and has content)
    $advancedSettingsParts = [];
    
    // Tempo/BPM (skip if default value 120)
    if (!empty($tempo) && $tempo != '120') {
        $advancedSettingsParts[] = $tempo . " BPM";
    }
    
    // Key
    if (!empty($key)) {
        $advancedSettingsParts[] = "key of " . $key;
    }
    
    // Scale
    if (!empty($scale)) {
        $advancedSettingsParts[] = $scale . " scale";
    }
    
    // Time Signature
    if (!empty($timeSignature)) {
        $advancedSettingsParts[] = $timeSignature . " time signature";
    }
    
    // Energy Level (slider 1-10)
    if (!empty($energy) && $energy != '7') {
        $energyLevel = intval($energy);
        if ($energyLevel <= 3) {
            $advancedSettingsParts[] = "Calm, low energy";
        } elseif ($energyLevel <= 5) {
            $advancedSettingsParts[] = "Moderate energy";
        } elseif ($energyLevel >= 8) {
            $advancedSettingsParts[] = "High energy, intense";
        } else {
            $advancedSettingsParts[] = "Energetic";
        }
    }
    
    // Excitement Level (slider 1-10)
    if (!empty($excitement) && $excitement != '6') {
        $excitementLevel = intval($excitement);
        if ($excitementLevel <= 3) {
            $advancedSettingsParts[] = "Relaxed, calm";
        } elseif ($excitementLevel >= 8) {
            $advancedSettingsParts[] = "Thrilling, exciting";
        }
    }
    
    // Mood
    if (!empty($mood)) {
        $advancedSettingsParts[] = ucfirst($mood) . " mood";
    }
    
    // Language
    if (!empty($language) && $language !== 'instrumental') {
        $advancedSettingsParts[] = ucfirst($language) . " language";
    }
    
    // Voice Type
    if (!empty($voiceType)) {
        switch ($voiceType) {
            case 'male':
                $advancedSettingsParts[] = "Male vocalist/voice";
                break;
            case 'female':
                $advancedSettingsParts[] = "Female vocalist/voice";
                break;
            case 'duet':
                $advancedSettingsParts[] = "Male and female duet vocals";
                break;
            case 'duet_female_male':
                $advancedSettingsParts[] = "Female and male duet vocals";
                break;
            case 'choir':
                $advancedSettingsParts[] = "Choir vocals";
                break;
            case 'child':
                $advancedSettingsParts[] = "Children's voices";
                break;
            case 'robot':
                $advancedSettingsParts[] = "Robotic/AI voice";
                break;
            default:
                $advancedSettingsParts[] = ucfirst($voiceType) . " voice";
        }
    }
    
    // Use Case
    if (!empty($useCase)) {
        $useCaseText = str_replace('-', ' ', $useCase);
        $advancedSettingsParts[] = ucwords($useCaseText) . " use case";
    }
    
    // Primary Instruments
    if (!empty($instruments)) {
        $advancedSettingsParts[] = ucfirst($instruments) . " as primary instrument";
    }
    
    // REMOVED: Most settings are redundant or not useful when added as text to the prompt
    // - Genre is already handled via the 'style' API parameter
    // - Technical parameters (energy, excitement, use case, instruments) don't help the AI
    // - Voice type and language should be mentioned naturally by the user if needed
    // - Tempo/key could be useful but are often redundant with user's natural description
    // Users should describe what they want naturally in their prompt instead
    // if (!empty($advancedSettingsParts)) {
    //     $settingsText = implode(", ", $advancedSettingsParts);
    //     $finalPrompt = rtrim($finalPrompt, '.') . '. ' . $settingsText . '.';
    //     error_log("Advanced mode: Incorporated settings naturally into prompt: " . count($advancedSettingsParts) . " settings");
    // }
}

// REMOVED: Voice type addition to prompt
// Users should mention voice type naturally in their prompt if they want it
// Adding it automatically can be redundant and clutter the prompt
// if (!empty($proVoiceType)) {
//     ... (removed voice type addition logic)
// }

// For pro mode: Incorporate all pro settings into the prompt
// Apply pro settings even without proPrompt text - these are the dropdown/slider settings
$hasProSettings = !empty($proGenre) || !empty($proTempo) || !empty($proKey) || !empty($proScale) || 
                  !empty($proTimeSignature) || !empty($proChordProgression) || !empty($proLeadInstrument) ||
                  !empty($proRhythmSection) || !empty($proHarmonySection) || !empty($proArrangement) ||
                  !empty($proVocalStyle) || !empty($proLanguage) || !empty($proMood) || !empty($proEnergy) ||
                  !empty($proSubGenre) || !empty($proDecade) || !empty($proComplexity) || !empty($proDensity) ||
                  !empty($proVoiceType) || !empty($proOctave) || !empty($proLyricTheme) || !empty($proRhymeScheme) ||
                  !empty($proHookFrequency) || !empty($proIntroLength) || !empty($proVerseChorusRatio) ||
                  !empty($proBridge) || !empty($proOutroStyle) || !empty($proBuildUps) || !empty($proTransitions) ||
                  !empty($proExcitement) || !empty($proDanceability) || !empty($proQuality) ||
                  !empty($proReverb) || !empty($proCompression) || !empty($proStereoWidth) ||
                  !empty($proBassLevel) || !empty($proMidLevel) || !empty($proTrebleLevel);

if ($hasProSettings) {
    $proSettingsParts = [];
    
    // Voice Type (IMPORTANT - add first for emphasis)
    if (!empty($proVoiceType)) {
        switch ($proVoiceType) {
            case 'male':
                $proSettingsParts[] = "Male vocalist/voice";
                break;
            case 'female':
                $proSettingsParts[] = "Female vocalist/voice";
                break;
            case 'duet':
                $proSettingsParts[] = "Male and female duet vocals";
                break;
            case 'duet_female_male':
                $proSettingsParts[] = "Female and male duet vocals";
                break;
            case 'choir':
                $proSettingsParts[] = "Choir vocals";
                break;
            case 'child':
                $proSettingsParts[] = "Children's voices";
                break;
            case 'robot':
                $proSettingsParts[] = "Robotic/AI voice";
                break;
            case 'whisper':
                $proSettingsParts[] = "Whispered vocals";
                break;
            case 'screaming':
                $proSettingsParts[] = "Screaming vocals";
                break;
            case 'rapping':
                $proSettingsParts[] = "Rapping vocals";
                break;
            case 'singing':
                $proSettingsParts[] = "Singing vocals";
                break;
            case 'spoken':
                $proSettingsParts[] = "Spoken word vocals";
                break;
            default:
                $proSettingsParts[] = ucfirst($proVoiceType) . " voice";
        }
    }
    
    // Genre (most important - already handled in style, but add to prompt for clarity)
    if (!empty($proGenre)) {
        $proSettingsParts[] = ucfirst($proGenre) . " genre";
    }
    
    // Tempo/BPM (skip if default value 120)
    if (!empty($proTempo) && $proTempo != '120') {
        $proSettingsParts[] = $proTempo . " BPM";
    }
    
    // Key
    if (!empty($proKey)) {
        $proSettingsParts[] = "key of " . $proKey;
    }
    
    // Scale
    if (!empty($proScale)) {
        $proSettingsParts[] = $proScale . " scale";
    }
    
    // Time Signature
    if (!empty($proTimeSignature)) {
        $proSettingsParts[] = $proTimeSignature . " time signature";
    }
    
    // Chord Progression
    if (!empty($proChordProgression) && $proChordProgression !== 'custom') {
        $proSettingsParts[] = $proChordProgression . " chord progression";
    }
    
    // Lead Instrument
    if (!empty($proLeadInstrument)) {
        $proSettingsParts[] = ucfirst($proLeadInstrument) . " as lead instrument";
    }
    
    // Rhythm Section
    if (!empty($proRhythmSection)) {
        $rhythmText = str_replace('-', ' ', $proRhythmSection);
        $proSettingsParts[] = ucwords($rhythmText) . " rhythm section";
    }
    
    // Harmony Section
    if (!empty($proHarmonySection)) {
        $harmonyText = str_replace('-', ' ', $proHarmonySection);
        $proSettingsParts[] = ucwords($harmonyText) . " harmony section";
    }
    
    // Arrangement
    if (!empty($proArrangement)) {
        $proSettingsParts[] = ucfirst($proArrangement) . " arrangement style";
    }
    
    // Vocal Style
    if (!empty($proVocalStyle)) {
        $vocalStyleText = str_replace('-', ' ', $proVocalStyle);
        $proSettingsParts[] = ucwords($vocalStyleText) . " vocal style";
    }
    
    // Language
    if (!empty($proLanguage) && $proLanguage !== 'instrumental') {
        $proSettingsParts[] = ucfirst($proLanguage) . " language";
    }
    
    // Mood
    if (!empty($proMood)) {
        $proSettingsParts[] = ucfirst($proMood) . " mood";
    }
    
    // Energy Level (slider 1-10)
    if (!empty($proEnergy) && $proEnergy != '7') {
        $energyLevel = intval($proEnergy);
        if ($energyLevel <= 3) {
            $proSettingsParts[] = "Low energy, calm";
        } elseif ($energyLevel <= 5) {
            $proSettingsParts[] = "Moderate energy";
        } elseif ($energyLevel >= 8) {
            $proSettingsParts[] = "High energy, intense";
        } else {
            $proSettingsParts[] = "Energetic";
        }
    }
    
    // Sub-genre
    if (!empty($proSubGenre)) {
        $proSettingsParts[] = ucfirst($proSubGenre) . " sub-genre";
    }
    
    // Decade style
    if (!empty($proDecade)) {
        $proSettingsParts[] = $proDecade . " style";
    }
    
    // Complexity
    if (!empty($proComplexity)) {
        $proSettingsParts[] = ucfirst($proComplexity) . " complexity";
    }
    
    // Density
    if (!empty($proDensity)) {
        $densityText = str_replace('-', ' ', $proDensity);
        $proSettingsParts[] = ucwords($densityText) . " track density";
    }
    
    // Octave
    if (!empty($proOctave)) {
        $proSettingsParts[] = "Octave " . $proOctave;
    }
    
    // Lyric Theme
    if (!empty($proLyricTheme)) {
        $proSettingsParts[] = ucfirst($proLyricTheme) . " themed lyrics";
    }
    
    // Rhyme Scheme
    if (!empty($proRhymeScheme)) {
        $proSettingsParts[] = ucfirst($proRhymeScheme) . " rhyme scheme";
    }
    
    // Hook Frequency
    if (!empty($proHookFrequency)) {
        $hookText = str_replace('-', ' ', $proHookFrequency);
        $proSettingsParts[] = ucwords($hookText) . " hooks";
    }
    
    // Intro Length
    if (!empty($proIntroLength)) {
        $proSettingsParts[] = ucfirst($proIntroLength) . " intro";
    }
    
    // Verse/Chorus Ratio
    if (!empty($proVerseChorusRatio)) {
        $ratioText = str_replace('-', ' ', $proVerseChorusRatio);
        $proSettingsParts[] = ucwords($ratioText) . " verse-chorus balance";
    }
    
    // Bridge
    if (!empty($proBridge)) {
        $bridgeText = str_replace('-', ' ', $proBridge);
        $proSettingsParts[] = ucwords($bridgeText) . " bridge section";
    }
    
    // Outro Style
    if (!empty($proOutroStyle)) {
        $outroText = str_replace('-', ' ', $proOutroStyle);
        $proSettingsParts[] = ucwords($outroText) . " outro";
    }
    
    // Build-ups
    if (!empty($proBuildUps)) {
        $buildText = str_replace('-', ' ', $proBuildUps);
        $proSettingsParts[] = ucwords($buildText) . " build-ups";
    }
    
    // Transitions
    if (!empty($proTransitions)) {
        $transText = str_replace('-', ' ', $proTransitions);
        $proSettingsParts[] = ucwords($transText) . " transitions";
    }
    
    // Excitement Level (slider 1-10)
    if (!empty($proExcitement) && $proExcitement != '5' && $proExcitement != '6') {
        $excitementLevel = intval($proExcitement);
        if ($excitementLevel <= 3) {
            $proSettingsParts[] = "Calm, subdued excitement";
        } elseif ($excitementLevel >= 8) {
            $proSettingsParts[] = "High excitement and intensity";
        }
    }
    
    // Danceability (slider 1-10)
    if (!empty($proDanceability) && $proDanceability != '5') {
        $danceLevel = intval($proDanceability);
        if ($danceLevel <= 3) {
            $proSettingsParts[] = "Low danceability, more listening-focused";
        } elseif ($danceLevel >= 7) {
            $proSettingsParts[] = "High danceability, danceable rhythm";
        }
    }
    
    // Production Quality
    if (!empty($proQuality)) {
        switch ($proQuality) {
            case 'demo':
                $proSettingsParts[] = "Demo quality production";
                break;
            case 'radio':
                $proSettingsParts[] = "Radio-ready production quality";
                break;
            case 'studio':
                $proSettingsParts[] = "Professional studio production quality";
                break;
            case 'master':
                $proSettingsParts[] = "Mastered, release-ready production quality";
                break;
        }
    }
    
    // Reverb Level (slider 0-10)
    if (!empty($proReverb) && $proReverb != '5') {
        $reverbLevel = intval($proReverb);
        if ($reverbLevel <= 2) {
            $proSettingsParts[] = "Dry, no reverb";
        } elseif ($reverbLevel <= 4) {
            $proSettingsParts[] = "Light reverb";
        } elseif ($reverbLevel <= 7) {
            $proSettingsParts[] = "Moderate reverb";
        } else {
            $proSettingsParts[] = "Heavy reverb, wet sound";
        }
    }
    
    // Compression (slider 0-10)
    if (!empty($proCompression) && $proCompression != '5') {
        $compressionLevel = intval($proCompression);
        if ($compressionLevel <= 2) {
            $proSettingsParts[] = "Light compression, dynamic";
        } elseif ($compressionLevel <= 4) {
            $proSettingsParts[] = "Moderate compression";
        } elseif ($compressionLevel <= 7) {
            $proSettingsParts[] = "Heavy compression";
        } else {
            $proSettingsParts[] = "Very heavy compression, punchy";
        }
    }
    
    // Stereo Width (slider 0-10)
    if (!empty($proStereoWidth) && $proStereoWidth != '5') {
        $stereoLevel = intval($proStereoWidth);
        if ($stereoLevel <= 2) {
            $proSettingsParts[] = "Mono or narrow stereo";
        } elseif ($stereoLevel <= 4) {
            $proSettingsParts[] = "Moderate stereo width";
        } elseif ($stereoLevel <= 7) {
            $proSettingsParts[] = "Wide stereo field";
        } else {
            $proSettingsParts[] = "Very wide stereo, spacious";
        }
    }
    
    // Bass Level (slider 0-10)
    if (!empty($proBassLevel) && $proBassLevel != '5') {
        $bassLevel = intval($proBassLevel);
        if ($bassLevel <= 2) {
            $proSettingsParts[] = "Low bass, minimal low end";
        } elseif ($bassLevel <= 4) {
            $proSettingsParts[] = "Moderate bass";
        } elseif ($bassLevel <= 7) {
            $proSettingsParts[] = "Prominent bass";
        } else {
            $proSettingsParts[] = "Heavy bass, bass-heavy mix";
        }
    }
    
    // Mid Level (slider 0-10)
    if (!empty($proMidLevel) && $proMidLevel != '5') {
        $midLevel = intval($proMidLevel);
        if ($midLevel <= 2) {
            $proSettingsParts[] = "Recessed mids, scooped";
        } elseif ($midLevel <= 4) {
            $proSettingsParts[] = "Moderate mid frequencies";
        } elseif ($midLevel <= 7) {
            $proSettingsParts[] = "Prominent mids";
        } else {
            $proSettingsParts[] = "Forward mids, mid-heavy";
        }
    }
    
    // Treble Level (slider 0-10)
    if (!empty($proTrebleLevel) && $proTrebleLevel != '5') {
        $trebleLevel = intval($proTrebleLevel);
        if ($trebleLevel <= 2) {
            $proSettingsParts[] = "Dark, low treble";
        } elseif ($trebleLevel <= 4) {
            $proSettingsParts[] = "Moderate treble";
        } elseif ($trebleLevel <= 7) {
            $proSettingsParts[] = "Bright, high treble";
        } else {
            $proSettingsParts[] = "Very bright, treble-heavy";
        }
    }
    
    // REMOVED: Most settings are redundant or not useful when added as text to the prompt
    // - Genre is already handled via the 'style' API parameter
    // - Technical mixing parameters (reverb, compression, stereo width, EQ) can't be controlled by the AI
    // - Structural parameters (intro length, verse/chorus ratio, etc.) are too technical
    // - Production quality, complexity, density are not actionable by the AI
    // - Voice type and language should be mentioned naturally by the user if needed
    // - Tempo/key could be useful but are often redundant with user's natural description
    // Users should describe what they want naturally in their prompt instead
    // The form fields remain for UI/UX but don't automatically clutter the prompt
    // if (!empty($proSettingsParts)) {
    //     $settingsText = implode(", ", $proSettingsParts);
    //     $finalPrompt = rtrim($finalPrompt, '.') . '. ' . $settingsText . '.';
    //     error_log("Pro mode: Incorporated settings naturally into prompt: " . count($proSettingsParts) . " settings");
    // }
}

// Check prompt length - if over 400 characters and customMode is not enabled, truncate to 400
// The API rejects prompts over 400 characters in non-custom mode, so we need to limit to 400
$promptLength = function_exists('mb_strlen') ? mb_strlen($finalPrompt) : strlen($finalPrompt);
error_log("Final prompt length: $promptLength characters");
error_log("Final prompt preview (first 200 chars): " . substr($finalPrompt, 0, 200));

if ($promptLength > 400 && ($customMode !== 'true' && $customMode !== true)) {
    if (function_exists('mb_substr')) {
        $finalPrompt = mb_substr($finalPrompt, 0, 400);
    } else {
        $finalPrompt = substr($finalPrompt, 0, 400);
    }
    $promptLength = function_exists('mb_strlen') ? mb_strlen($finalPrompt) : strlen($finalPrompt);
    error_log("WARNING: Prompt length exceeded 400 characters, truncated to $promptLength characters for non-custom mode");
}

// Use pro model if available, otherwise use regular model
$finalModel = !empty($proModel) ? $proModel : $model_name;

// Parse duration - handle formats like "5:10" (5 minutes 10 seconds), "5-6 minutes", or just seconds
function parseDuration($durationInput) {
    if (empty($durationInput)) {
        return null;
    }
    
    // If it's already a number, return it
    if (is_numeric($durationInput)) {
        return intval($durationInput);
    }
    
    // Try to parse natural language ranges like "5-6 minutes" or "5 to 6 min"
    // Use the higher end of the range to ensure we get closer to requested length
    if (preg_match('/(\d+)\s*[-–—to]\s*(\d+)\s*(?:minutes?|mins?|min)/i', $durationInput, $rangeMatches)) {
        $minMinutes = intval($rangeMatches[1]);
        $maxMinutes = intval($rangeMatches[2]);
        return $maxMinutes * 60; // Use higher end
    }
    
    // Try to parse single natural language like "5 minutes" or "about 5 min"
    if (preg_match('/(?:about|around|approximately|roughly)?\s*(\d+)\s*(?:minutes?|mins?|min)/i', $durationInput, $singleMatches)) {
        $minutes = intval($singleMatches[1]);
        return $minutes * 60;
    }
    
    // Try to parse "MM:SS" or "M:SS" format (handle spaces around colon like "5 : 10")
    $trimmed = trim($durationInput);
    // Remove spaces around colon
    $trimmed = preg_replace('/\s*:\s*/', ':', $trimmed);
    if (preg_match('/^(\d+):(\d+)$/', $trimmed, $matches)) {
        $minutes = intval($matches[1]);
        $seconds = intval($matches[2]);
        return ($minutes * 60) + $seconds;
    }
    
    // Try to parse "MM:SS:MS" format (minutes:seconds:milliseconds - ignore milliseconds, handle spaces)
    $trimmed = trim($durationInput);
    // Remove spaces around colons
    $trimmed = preg_replace('/\s*:\s*/', ':', $trimmed);
    if (preg_match('/^(\d+):(\d+):(\d+)$/', $trimmed, $matches)) {
        $minutes = intval($matches[1]);
        $seconds = intval($matches[2]);
        return ($minutes * 60) + $seconds;
    }
    
    // Try to parse seconds ranges like "300-360 seconds"
    if (preg_match('/(\d+)\s*[-–—to]\s*(\d+)\s*(?:seconds?|secs?|sec)/i', $durationInput, $secRangeMatches)) {
        $minSeconds = intval($secRangeMatches[1]);
        $maxSeconds = intval($secRangeMatches[2]);
        return $maxSeconds; // Use higher end
    }
    
    // Try to parse single seconds like "360 seconds"
    if (preg_match('/(?:about|around|approximately|roughly)?\s*(\d+)\s*(?:seconds?|secs?|sec)/i', $durationInput, $secSingleMatches)) {
        return intval($secSingleMatches[1]);
    }
    
    // If it's a string with just numbers, try to parse as integer
    $cleaned = preg_replace('/[^0-9]/', '', $durationInput);
    if (!empty($cleaned) && is_numeric($cleaned)) {
        return intval($cleaned);
    }
    
    return null;
}

// Use pro duration if available, otherwise use regular duration
// For pro mode: prioritize proDuration field, but also check duration (which JavaScript may have populated)
$parsedProDuration = !empty($proDuration) ? parseDuration($proDuration) : null;
$parsedDuration = parseDuration($duration);

// If pro mode is active (proPrompt exists), prioritize pro duration or duration field
// This ensures pro mode gets the correct duration even if proDuration field wasn't sent
if (!empty($proPrompt)) {
    // Pro mode: use proDuration if available, otherwise use duration (which JavaScript should have populated)
    $finalDuration = $parsedProDuration ?? $parsedDuration ?? 360;
    error_log("Pro mode detected - Duration parsing: proDuration='$proDuration', duration='$duration', Parsed Pro: " . ($parsedProDuration ?? 'null') . ", Parsed: $parsedDuration, Final: $finalDuration");
} else {
    // Regular mode: use proDuration if available, otherwise use regular duration
    $finalDuration = $parsedProDuration ?? $parsedDuration ?? 360;
}

error_log("Duration parsing - Input duration: '$duration', Pro duration: '$proDuration', Parsed: $parsedDuration, Parsed Pro: " . ($parsedProDuration ?? 'null') . ", Final: $finalDuration");

// Validate duration based on model version (per API.box documentation)
// V3_5/V4: max 4 minutes (240 seconds)
// V4_5/V4_5PLUS/V5: max 8 minutes (480 seconds)
$maxDuration = 480; // Default to 8 minutes for latest models
if ($finalModel === 'V3_5' || $finalModel === 'V4') {
    $maxDuration = 240; // 4 minutes max
} else {
    // V4_5, V4_5PLUS, V5 all support up to 8 minutes
    $maxDuration = 480; // 8 minutes max
}

// If requested duration exceeds model limit, automatically upgrade to a model that supports it
// This ensures users get the duration they requested
if ($finalDuration > $maxDuration) {
    if ($finalModel === 'V3_5' || $finalModel === 'V4') {
        // Upgrade to V4_5PLUS which supports up to 8 minutes
        $finalModel = 'V4_5PLUS';
        $maxDuration = 480;
        error_log("WARNING: Duration ($finalDuration) exceeds model limit for $finalModel, automatically upgrading to V4_5PLUS to support requested duration");
    } else {
        // Already using a model that should support 8 minutes, but duration still exceeds
        // Cap it to the max
        error_log("WARNING: Duration ($finalDuration) exceeds model limit ($maxDuration) for $finalModel, capping to $maxDuration");
        $finalDuration = $maxDuration;
    }
}

// Ensure duration is at least 60 seconds (1 minute) and within model limits
// Default to 6 minutes (360 seconds) - in the 5-7 minute range
if (empty($finalDuration) || $finalDuration < 60) {
    error_log("WARNING: Invalid duration ($finalDuration), defaulting to 360 seconds (6 minutes)");
    $finalDuration = 360;
}

// For Simple Mode: Smart duration handling with keyword detection and 5-7 minute range
// This provides optimal song length for Simple Mode users
$isSimpleMode = empty($advancedPrompt) && empty($proPrompt);
if ($isSimpleMode) {
    $minSimpleDuration = 300; // 5 minutes
    $maxSimpleDuration = 420; // 7 minutes
    $defaultSimpleDuration = 360; // 6 minutes (middle of range)
    
    // Smart keyword detection for duration preference (Option 4)
    $promptLower = strtolower($finalPrompt);
    $durationKeyword = null;
    
    // Check for "short" keywords - prefer 5 minutes
    if (preg_match('/\b(short|brief|quick|fast|minimal|compact)\b/i', $promptLower)) {
        $durationKeyword = 'short';
        error_log("Simple Mode: Detected 'short' keyword in prompt, preferring 5 minutes");
    }
    // Check for "long" keywords - prefer 7 minutes
    elseif (preg_match('/\b(long|extended|full|complete|extensive|lengthy)\b/i', $promptLower)) {
        $durationKeyword = 'long';
        error_log("Simple Mode: Detected 'long' keyword in prompt, preferring 7 minutes");
    }
    
    // Priority order: 1) Extracted duration from prompt, 2) Keyword preference, 3) Default
    // If duration was extracted from prompt, clamp it to 5-7 minutes (Option 3)
    if (!empty($extractedMetadata['duration'])) {
        $extractedDuration = $extractedMetadata['duration'];
        if ($extractedDuration < $minSimpleDuration) {
            error_log("Simple Mode: Extracted duration ($extractedDuration) below 5 minutes, clamping to $minSimpleDuration seconds (5 minutes)");
            $finalDuration = $minSimpleDuration;
        } elseif ($extractedDuration > $maxSimpleDuration) {
            error_log("Simple Mode: Extracted duration ($extractedDuration) above 7 minutes, clamping to $maxSimpleDuration seconds (7 minutes)");
            $finalDuration = $maxSimpleDuration;
        } else {
            // Extracted duration is already in range, use it
            $finalDuration = $extractedDuration;
            error_log("Simple Mode: Using extracted duration ($extractedDuration) which is within 5-7 minute range");
        }
    }
    // If no explicit duration was extracted but we have a keyword preference, use it (Option 4)
    elseif ($durationKeyword !== null) {
        if ($durationKeyword === 'short') {
            $finalDuration = $minSimpleDuration; // 5 minutes
            error_log("Simple Mode: Using keyword preference 'short' -> 5 minutes (300 seconds)");
        } elseif ($durationKeyword === 'long') {
            $finalDuration = $maxSimpleDuration; // 7 minutes
            error_log("Simple Mode: Using keyword preference 'long' -> 7 minutes (420 seconds)");
        }
    }
    // If no duration extracted and no keyword preference, use default 6 minutes
    else {
        // Only set default if finalDuration is still at the generic default (360) or invalid
        if ($finalDuration == 360 || $finalDuration < 60) {
            $finalDuration = $defaultSimpleDuration;
            error_log("Simple Mode: No duration specified, using default 6 minutes (360 seconds)");
        } else {
            // finalDuration was set from form, but clamp it to 5-7 minutes
            if ($finalDuration < $minSimpleDuration) {
                error_log("Simple Mode: Form duration ($finalDuration) below 5 minutes, adjusting to $minSimpleDuration seconds");
                $finalDuration = $minSimpleDuration;
            } elseif ($finalDuration > $maxSimpleDuration) {
                error_log("Simple Mode: Form duration ($finalDuration) above 7 minutes, adjusting to $maxSimpleDuration seconds");
                $finalDuration = $maxSimpleDuration;
            }
        }
    }
    
    // Final safety clamp - ensure it's always in 5-7 minute range regardless of how we got here
    if ($finalDuration < $minSimpleDuration) {
        error_log("Simple Mode: Final safety clamp - Duration ($finalDuration) below 5 minutes, adjusting to $minSimpleDuration seconds");
        $finalDuration = $minSimpleDuration;
    } elseif ($finalDuration > $maxSimpleDuration) {
        error_log("Simple Mode: Final safety clamp - Duration ($finalDuration) above 7 minutes, adjusting to $maxSimpleDuration seconds");
        $finalDuration = $maxSimpleDuration;
    }
    
    error_log("Simple Mode: Final duration set to $finalDuration seconds (" . round($finalDuration / 60, 1) . " minutes)");
}

error_log("Final duration being sent to API: $finalDuration seconds (" . round($finalDuration / 60, 1) . " minutes) for model $finalModel (max: $maxDuration)");

// Validate input - but this should never be empty at this point due to fallback above
// If it is empty here, something went wrong with the fallback logic
if (empty($finalPrompt)) {
    error_log("create_music.php: Validation failed - empty prompt after all processing");
    error_log("DEBUG: prompt='$prompt', advancedPrompt='$advancedPrompt', proPrompt='$proPrompt', genre='$genre', proGenre='$proGenre'");
    // Don't show error - just use a default prompt to prevent blocking
    $finalPrompt = !empty($genre) ? $genre : (!empty($proGenre) ? $proGenre : 'Electronic music track');
    error_log("Using emergency fallback prompt: '$finalPrompt'");
}

// Calculate credit cost - ALL MODES COST 1 CREDIT
$creditCost = 1; // All modes (Simple, Advanced, Pro) cost 1 credit
error_log("create_music.php: Credit cost set to 1 credit for all modes");

// Check if user has subscription (monthly limit) or needs credits
require_once __DIR__ . '/utils/subscription_helpers.php';
$track_check = canCreateTrack($_SESSION['user_id']);

// CRITICAL LOGGING: Track which system is being used
error_log("create_music.php: canCreateTrack() result for user {$_SESSION['user_id']}: " . json_encode($track_check));

if (!$track_check['allowed']) {
    // User has reached monthly limit or subscription issue
    error_log("Track creation blocked for user {$_SESSION['user_id']}: " . ($track_check['message'] ?? 'Monthly limit reached'));
    $_SESSION['error'] = $track_check['message'] ?? "You've reached your monthly track limit. Please wait for next month or upgrade your plan.";
    header('Location: index.php#create');
    exit;
}

// If user is on subscription system, we'll increment usage after track creation
// Otherwise, check credits for credit-based plans
if (!isset($track_check['system']) || $track_check['system'] !== 'credits') {
    // User is on subscription - monthly limit already checked, will increment after creation
    $system_used = $track_check['system'] ?? 'subscription (default)';
    error_log("create_music.php: User on subscription plan, monthly limit check passed. System: {$system_used}, tracks_used: " . ($track_check['tracks_used'] ?? 'N/A') . ", track_limit: " . ($track_check['track_limit'] ?? 'N/A'));
} else {
    // User is on credit system - check credits
    error_log("create_music.php: User on credit system. Checking user credits, cost: $creditCost");
    $stmt = $pdo->prepare("SELECT credits, plan FROM users WHERE id = ?");
    $stmt->execute([$_SESSION['user_id']]);
    $user = $stmt->fetch(PDO::FETCH_ASSOC);
    
    // Ensure credits is an integer for comparison
    $userCredits = isset($user['credits']) ? (int)$user['credits'] : 0;
    error_log("create_music.php: User credits check complete, user credits: $userCredits (raw: " . ($user['credits'] ?? 'N/A') . ")");
    
    if (!$user || $userCredits < $creditCost) {
        error_log("Insufficient credits for user {$_SESSION['user_id']}: has $userCredits, needs $creditCost");
        $_SESSION['error'] = "Insufficient credits. You need $creditCost credits to generate music in this mode. Please <a href='/pricing.php' style='color: #667eea; text-decoration: underline;'>purchase credits</a> to continue.";
        $_SESSION['error_allow_html'] = true; // Flag to allow HTML in error message
        header('Location: index.php#create');
        exit;
    }
}

// Create track record
$temp_task_id = 'temp_' . time() . '_' . $_SESSION['user_id'] . '_' . uniqid();

// Determine final genre that will be sent to API (we'll set this after genre extraction)
$finalGenreForMetadata = $genre ?: $proGenre ?: null;

try {
    $metadata = json_encode([
        'customMode' => $customMode === 'true',
        'model_name' => $finalModel,
        'duration' => $finalDuration,
        'genre' => $finalGenreForMetadata, // Will be updated after genre extraction
        'key' => $key,
        'tempo' => $tempo,
        'mood' => $mood,
        'instrumental' => $instrumental,
        'energy' => $energy,
        'excitement' => $excitement,
        'scale' => $scale,
        'tags' => $tags,
        'timeSignature' => $timeSignature,
        'language' => $language,
        'voiceType' => $voiceType,
        'useCase' => $useCase,
        'instruments' => $instruments,
        // Pro Mode fields
        'proTitle' => $proTitle,
        'proModel' => $proModel,
        'proVariations' => $proVariations,
        'proKey' => $proKey,
        'proScale' => $proScale,
        'proTimeSignature' => $proTimeSignature,
        'proTempo' => $proTempo,
        'proChordProgression' => $proChordProgression,
        'proOctave' => $proOctave,
        'proGenre' => $proGenre,
        'proSubGenre' => $proSubGenre,
        'proDecade' => $proDecade,
        'proLeadInstrument' => $proLeadInstrument,
        'proRhythmSection' => $proRhythmSection,
        'proHarmonySection' => $proHarmonySection,
        'proArrangement' => $proArrangement,
        'proComplexity' => $proComplexity,
        'proDensity' => $proDensity,
        'proReverb' => $proReverb,
        'proCompression' => $proCompression,
        'proStereoWidth' => $proStereoWidth,
        'proBassLevel' => $proBassLevel,
        'proMidLevel' => $proMidLevel,
        'proTrebleLevel' => $proTrebleLevel,
        'proVoiceType' => $proVoiceType,
        'proLanguage' => $proLanguage,
        'proVocalStyle' => $proVocalStyle,
        'proLyricTheme' => $proLyricTheme,
        'proRhymeScheme' => $proRhymeScheme,
        'proHookFrequency' => $proHookFrequency,
        'proIntroLength' => $proIntroLength,
        'proVerseChorusRatio' => $proVerseChorusRatio,
        'proBridge' => $proBridge,
        'proOutroStyle' => $proOutroStyle,
        'proBuildUps' => $proBuildUps,
        'proTransitions' => $proTransitions,
        'proQuality' => $proQuality,
        'proMood' => $proMood,
        'proEnergy' => $proEnergy,
        'proExcitement' => $proExcitement,
        'proDanceability' => $proDanceability,
        'proPrompt' => $proPrompt,
        'proTags' => $proTags
    ], (defined('JSON_UNESCAPED_UNICODE') ? JSON_UNESCAPED_UNICODE : 0) | (defined('JSON_UNESCAPED_SLASHES') ? JSON_UNESCAPED_SLASHES : 0));
    
    if ($metadata === false) {
        $error_msg = function_exists('json_last_error_msg') ? json_last_error_msg() : 'Unknown JSON error (code: ' . json_last_error() . ')';
        throw new Exception('Failed to encode metadata: ' . $error_msg);
    }
} catch (Exception $e) {
    error_log("Metadata encoding error: " . $e->getMessage());
    $_SESSION['error'] = 'Error processing your request. Please try again.';
    header('Location: index.php#create');
    exit;
}

error_log("create_music.php: Creating track record in database");
error_log("create_music.php: user_id: " . $_SESSION['user_id']);
error_log("create_music.php: temp_task_id: $temp_task_id");
error_log("create_music.php: finalTitle length: " . strlen($finalTitle));
error_log("create_music.php: finalPrompt length: " . strlen($finalPrompt));
error_log("create_music.php: metadata length: " . strlen($metadata));
try {
    $stmt = $pdo->prepare("
        INSERT INTO music_tracks (user_id, task_id, title, prompt, music_type, status, metadata, is_public, created_at) 
        VALUES (?, ?, ?, ?, 'music', 'processing', ?, 0, NOW())
    ");
    $result = $stmt->execute([$_SESSION['user_id'], $temp_task_id, $finalTitle, $finalPrompt, $metadata]);
    
    if (!$result) {
        $errorInfo = $stmt->errorInfo();
        error_log("Database insert failed - SQL error: " . print_r($errorInfo, true));
        throw new Exception('Database insert failed: ' . ($errorInfo[2] ?? 'Unknown SQL error'));
    }
    
    $track_id = $pdo->lastInsertId();
    
    if (!$track_id) {
        $errorInfo = $stmt->errorInfo();
        error_log("lastInsertId returned false/0 - SQL error: " . print_r($errorInfo, true));
        throw new Exception('Failed to create track record - lastInsertId returned false/0. SQL Error: ' . ($errorInfo[2] ?? 'Unknown'));
    }
    error_log("create_music.php: Track record created successfully, track_id: $track_id");
} catch (PDOException $e) {
    error_log("Database insert PDO error: " . $e->getMessage());
    error_log("Database insert PDO error code: " . $e->getCode());
    error_log("Database insert error trace: " . $e->getTraceAsString());
    $_SESSION['error'] = 'Error saving your track: ' . htmlspecialchars($e->getMessage()) . ' Please try again.';
    ob_end_clean();
    header('Location: index.php#create');
    exit;
} catch (Exception $e) {
    error_log("Database insert error: " . $e->getMessage());
    error_log("Database insert error trace: " . $e->getTraceAsString());
    $_SESSION['error'] = 'Error saving your track: ' . htmlspecialchars($e->getMessage()) . ' Please try again.';
    ob_end_clean();
    header('Location: index.php#create');
    exit;
}

// CRITICAL: DO NOT deduct credits yet - wait for successful API response
// Credits will be deducted AFTER API confirms track creation
$shouldDeductCredits = (!isset($track_check['system']) || $track_check['system'] === 'credits');
$shouldIncrementUsage = (!isset($track_check['system']) || $track_check['system'] !== 'credits');

// CRITICAL LOGGING: Track which system will be used
error_log("create_music.php: System decision for user {$_SESSION['user_id']}: shouldDeductCredits=" . ($shouldDeductCredits ? 'true' : 'false') . ", shouldIncrementUsage=" . ($shouldIncrementUsage ? 'true' : 'false') . ", track_check system: " . ($track_check['system'] ?? 'not set'));

// Call the music generation API
$api_url = 'https://api.api.box/api/v1/generate';
// Build API data - start with prompt, we'll set style later if needed
// NOTE: API.box playground might use boolean values instead of strings - let's try both formats
$isCustomModeBool = ($customMode === 'true' || $customMode === true);
// CRITICAL: Check instrumental value - it should be 'true' string from form, or actual boolean true
$isInstrumentalBool = ($instrumental === 'true' || $instrumental === true || $instrumental === 1 || $instrumental === '1');
error_log("create_music.php: Instrumental conversion - input: '$instrumental', boolean: " . ($isInstrumentalBool ? 'TRUE' : 'FALSE'));

$api_data = [
    'prompt' => $finalPrompt,
    'model' => $finalModel,
    'customMode' => $isCustomModeBool, // Use boolean instead of string - might improve quality
    'instrumental' => $isInstrumentalBool, // Use boolean instead of string - CRITICAL for API.box
    'callBackUrl' => 'https://soundstudiopro.com/callback.php'
];
error_log("create_music.php: API data instrumental value: " . ($api_data['instrumental'] ? 'TRUE (boolean)' : 'FALSE (boolean)'));

// DO NOT set style here - it will be set in customMode section if needed
// This prevents "Pop" from being set as default

// Duration: ALWAYS send duration to API.box
// NOTE: API.box may sometimes return shorter songs than requested, especially for certain styles
// This is an API limitation, not a code issue - we always send the requested duration
// CRITICAL: Ensure duration is an integer (not string) for API compatibility
$api_data['duration'] = (int)$finalDuration;
error_log("Setting duration in api_data: " . $api_data['duration'] . " seconds (type: " . gettype($api_data['duration']) . ")");
if (!empty($proPrompt)) {
    error_log("PRO MODE: Duration for pro mode with lyrics: " . $api_data['duration'] . " seconds (" . round($api_data['duration'] / 60, 1) . " minutes)");
}

// According to API.box docs:
// - If customMode=true AND instrumental=true: style and title are REQUIRED
// - If customMode=true AND instrumental=false: style, prompt, and title are REQUIRED
// - If customMode=false: only prompt is required, other params should be empty

// Already set above as boolean
$isCustomMode = $isCustomModeBool;
$isInstrumental = $isInstrumentalBool;

if ($isCustomMode) {
    // Custom mode: style and title are REQUIRED per API.box docs
    // Priority: 1) Form genre field, 2) Pro genre field, 3) Extract from structured metadata, 4) Extract from prompt, 5) Default
    $extractedGenre = '';
    
    // First, try to use style from extracted metadata (if structured format was used)
    if (!empty($extractedMetadata['style'])) {
        $styleText = $extractedMetadata['style'];
        error_log("Using Style from extracted metadata: '$styleText'");
        
        $styleLower = strtolower($styleText);
        
        // Look for multi-word genres first (higher priority) - check for exact matches
        $multiWordGenres = [
            'deep house', 'progressive house', 'tech house', 'deep techno', 'ambient techno',
            'psy trance', 'psytrance', 'psy chill', 'drum and bass', 'drum & bass',
            'hip hop', 'r&b', 'lo-fi', 'lofi', 'trip hop', 'deep techno', 'ambient house'
        ];
        
        foreach ($multiWordGenres as $multiGenre) {
            if (strpos($styleLower, $multiGenre) !== false) {
                $extractedGenre = ucwords($multiGenre);
                error_log("Extracted multi-word genre from metadata: '$extractedGenre'");
                break;
            }
        }
        
        // If no multi-word genre found, look for single-word genres in order of preference
        if (empty($extractedGenre)) {
            // Priority order: house, techno, trance, ambient, electronic, dance
            // Removed 'experimental' - too broad and often incorrectly matched
            $singleWordGenres = ['house', 'techno', 'trance', 'ambient', 'electronic', 'dance', 'progressive', 'chill', 'dubstep', 'trap', 'edm', 'pop', 'rock', 'jazz', 'reggae', 'country', 'folk', 'blues', 'funk', 'disco', 'metal', 'punk', 'soul', 'gospel', 'latin', 'world'];
            
            // Check for "deep" followed by a genre (e.g., "deep house", "deep techno")
            if (preg_match('/deep\s+(house|techno|trance|ambient)/i', $styleText, $deepMatch)) {
                $extractedGenre = 'Deep ' . ucwords($deepMatch[1]);
                error_log("Extracted 'Deep' genre from metadata: '$extractedGenre'");
            } else {
                // Look for genre keywords in the style text
                foreach ($singleWordGenres as $genre) {
                    // Use word boundaries to match whole words
                    if (preg_match('/\b' . preg_quote($genre, '/') . '\b/i', $styleText)) {
                        $extractedGenre = ucwords($genre);
                        error_log("Extracted single-word genre from metadata: '$extractedGenre'");
                        break;
                    }
                }
            }
        }
    }
    
    // If no genre extracted from metadata, try to extract from prompt (e.g., "Style: deep house ambient techno")
    // Handle formats like: "Style: deepmale vocals deep house ambient techno tone drone dance electronic"
    if (empty($extractedGenre) && preg_match('/style[:\s]+([^:|\n]+?)(?:\s*[:\-|]|$)/i', $finalPrompt, $matches)) {
        $styleText = trim($matches[1]);
        error_log("Found Style field in prompt: '$styleText'");
        
        $styleLower = strtolower($styleText);
        
        // Look for multi-word genres first (higher priority) - check for exact matches
        $multiWordGenres = [
            'deep house', 'progressive house', 'tech house', 'deep techno', 'ambient techno',
            'psy trance', 'psytrance', 'psy chill', 'drum and bass', 'drum & bass',
            'hip hop', 'r&b', 'lo-fi', 'lofi', 'trip hop', 'deep techno', 'ambient house'
        ];
        
        foreach ($multiWordGenres as $multiGenre) {
            if (strpos($styleLower, $multiGenre) !== false) {
                $extractedGenre = ucwords($multiGenre);
                error_log("Extracted multi-word genre from prompt: '$extractedGenre'");
                break;
            }
        }
        
        // If no multi-word genre found, look for single-word genres in order of preference
        if (empty($extractedGenre)) {
            // Priority order: house, techno, trance, ambient, electronic, dance
            // Removed 'experimental' - too broad and often incorrectly matched
            $singleWordGenres = ['house', 'techno', 'trance', 'ambient', 'electronic', 'dance', 'progressive', 'chill', 'dubstep', 'trap', 'edm', 'pop', 'rock', 'jazz', 'reggae', 'country', 'folk', 'blues', 'funk', 'disco', 'metal', 'punk', 'soul', 'gospel', 'latin', 'world'];
            
            // Check for "deep" followed by a genre (e.g., "deep house", "deep techno")
            if (preg_match('/deep\s+(house|techno|trance|ambient)/i', $styleText, $deepMatch)) {
                $extractedGenre = 'Deep ' . ucwords($deepMatch[1]);
                error_log("Extracted 'Deep' genre from prompt: '$extractedGenre'");
            } else {
                // Look for genre keywords in the style text
                foreach ($singleWordGenres as $genre) {
                    // Use word boundaries to match whole words
                    if (preg_match('/\b' . preg_quote($genre, '/') . '\b/i', $styleText)) {
                        $extractedGenre = ucwords($genre);
                        error_log("Extracted single-word genre from prompt: '$extractedGenre'");
                        break;
                    }
                }
            }
        }
        
        if (!empty($extractedGenre)) {
            error_log("Final extracted genre from prompt Style field: '$extractedGenre'");
        }
    }
    
    // Use provided genre, otherwise extracted genre, otherwise default
    if (!empty($genre)) {
        $api_data['style'] = ucfirst($genre);
        error_log("Using genre from form field: '$genre' -> '" . $api_data['style'] . "'");
    } elseif (!empty($proGenre)) {
        // Convert proGenre to proper format for API (e.g., "pop" -> "Pop", "hip-hop" -> "Hip-Hop")
        $styleGenre = $proGenre;
        // Handle special cases with hyphens
        if (strpos($styleGenre, '-') !== false) {
            $parts = explode('-', $styleGenre);
            $styleGenre = implode('-', array_map('ucfirst', $parts));
        } else {
            $styleGenre = ucfirst($styleGenre);
        }
        $api_data['style'] = $styleGenre;
        error_log("Using genre from pro form field: '$proGenre' -> '" . $api_data['style'] . "'");
    } elseif (!empty($extractedGenre)) {
        $api_data['style'] = $extractedGenre;
        error_log("Using extracted genre from prompt: '$extractedGenre'");
    } else {
        // Default to "Pop" for pro mode (same as basic mode for consistency)
        $api_data['style'] = 'Pop';
        error_log("No genre provided in pro mode, using default style: Pop");
    }
    
    // Title is also required in custom mode
    // API requires title to be max 80 characters - truncate if needed
    if (!empty($finalTitle)) {
        // Truncate title to 80 characters (using mb functions for proper multi-byte handling)
        $titleLength = function_exists('mb_strlen') ? mb_strlen($finalTitle, 'UTF-8') : strlen($finalTitle);
        if ($titleLength > 80) {
            $api_data['title'] = function_exists('mb_substr') ? mb_substr($finalTitle, 0, 80, 'UTF-8') : substr($finalTitle, 0, 80);
            error_log("WARNING: Title exceeded 80 characters ($titleLength), truncated to: '" . $api_data['title'] . "'");
        } else {
            $api_data['title'] = $finalTitle;
        }
    } else {
        // API requires title in custom mode, generate one from prompt (max 80 chars per docs)
        // Try to create a meaningful title instead of just truncating
        
        // First, try to extract key phrases from the prompt
        $generatedTitle = '';
        
        // Look for genre/mood/theme keywords
        $keywords = [];
        
        // Extract genre mentions (e.g., "Genre : Électro-chill", "Genre: Pop")
        if (preg_match('/genre\s*[:\-]?\s*([^\n•]+)/i', $finalPrompt, $matches)) {
            $genrePart = trim($matches[1]);
            // Clean up genre (remove "tropical", "ensoleillée" etc, keep main genre)
            $genrePart = preg_replace('/\s*(tropical|ensoleillée|douce|légère).*$/i', '', $genrePart);
            $genrePart = trim($genrePart);
            if (!empty($genrePart) && mb_strlen($genrePart) <= 30) {
                $keywords[] = $genrePart;
            }
        }
        
        // Extract mood/ambiance (e.g., "Ambiance : Île tropicale", "Humeur : Romantique")
        if (preg_match('/(?:ambiance|humeur|mood)\s*[:\-]?\s*([^\n•]+)/i', $finalPrompt, $matches)) {
            $moodPart = trim($matches[1]);
            // Take first meaningful word/phrase (up to 2-3 words)
            $moodWords = preg_split('/[,\s]+/', $moodPart);
            $moodPhrase = implode(' ', array_slice($moodWords, 0, 2));
            if (!empty($moodPhrase) && mb_strlen($moodPhrase) <= 25) {
                $keywords[] = $moodPhrase;
            }
        }
        
        // Extract location/theme (e.g., "Île tropicale", "La Isla Bonita", "inspiré de")
        if (preg_match('/inspiré\s+de\s+["\']([^"\']+)["\']/i', $finalPrompt, $matches)) {
            // Extract song/theme name from "inspiré de" pattern
            $themeName = trim($matches[1]);
            if (!empty($themeName) && mb_strlen($themeName) <= 30) {
                $keywords[] = $themeName;
            }
        } elseif (preg_match('/\b(isla|île|island|tropical|plage|beach)\s+([^\n•]{0,20})/i', $finalPrompt, $matches)) {
            $locationPart = trim($matches[0]);
            if (!empty($locationPart) && mb_strlen($locationPart) <= 25) {
                $keywords[] = $locationPart;
            }
        }
        
        // If we found keywords, combine them into a title
        if (!empty($keywords)) {
            $generatedTitle = implode(' • ', array_slice($keywords, 0, 2)); // Max 2 keywords
        } else {
            // Fallback: extract first meaningful sentence or phrase (not just first 77 chars)
            // Try to find a complete phrase or sentence
            $firstSentence = preg_split('/[.!?\n•]/', $finalPrompt)[0];
            $firstSentence = trim($firstSentence);
            
            // Remove common prefixes
            $firstSentence = preg_replace('/^(🎶\s*)?(prompt|original prompt|description|musical|inspiré)[:\s]+/i', '', $firstSentence);
            $firstSentence = trim($firstSentence);
            
            // If first sentence is too long, try to extract key words
            if (mb_strlen($firstSentence) > 60) {
                // Extract first 3-4 meaningful words
                $words = preg_split('/\s+/', $firstSentence);
                $keyWords = array_slice($words, 0, 4);
                $firstSentence = implode(' ', $keyWords);
            }
            
            $generatedTitle = $firstSentence ?: 'Generated Track';
        }
        
        // Clean up title
        $generatedTitle = trim($generatedTitle);
        $generatedTitle = preg_replace('/\s+/', ' ', $generatedTitle); // Remove extra spaces
        $generatedTitle = $generatedTitle ?: 'Generated Track';
        
        // Ensure generated title is max 80 characters (API requirement)
        $genTitleLength = function_exists('mb_strlen') ? mb_strlen($generatedTitle, 'UTF-8') : strlen($generatedTitle);
        if ($genTitleLength > 80) {
            // Smart truncation: try to cut at a word boundary
            $truncated = function_exists('mb_substr') ? mb_substr($generatedTitle, 0, 77, 'UTF-8') : substr($generatedTitle, 0, 77);
            // Remove partial word at the end
            $truncated = preg_replace('/\s+\S*$/', '', $truncated);
            $api_data['title'] = $truncated;
            error_log("Generated title exceeded 80 characters ($genTitleLength), truncated to: '" . $api_data['title'] . "'");
        } else {
            $api_data['title'] = $generatedTitle;
        }
        error_log("Generated title from prompt: '" . $api_data['title'] . "' (length: " . (function_exists('mb_strlen') ? mb_strlen($api_data['title'], 'UTF-8') : strlen($api_data['title'])) . ")");
    }
} else {
    // Non-custom mode: Based on successful API requests, we can send style even in non-custom mode
    // The successful format shows: customMode:false, style:"Pop", title:""
    // IMPORTANT: Always use "Pop" as style in non-custom mode - the API extracts keywords from prompt
    // The style parameter doesn't need to match the prompt keywords - "Pop" works best
    $api_data['style'] = 'Pop';
    error_log("Non-custom mode: Using 'Pop' as style (API will extract keywords from prompt)");
    // Send empty title string (not unset) to match successful request format
    $api_data['title'] = '';
}

// CRITICAL: API.box ONLY accepts these parameters:
// - prompt, model, style (if customMode=true), title (if customMode=true), customMode, instrumental, duration, callBackUrl
// DO NOT send any other parameters - they interfere with the API and degrade quality!

// Remove any parameters that API.box doesn't support
$allowedParams = ['prompt', 'model', 'style', 'title', 'customMode', 'instrumental', 'duration', 'callBackUrl'];
$clean_api_data = [];
foreach ($allowedParams as $param) {
    if (isset($api_data[$param])) {
        $clean_api_data[$param] = $api_data[$param];
    }
}
// CRITICAL: Ensure duration is ALWAYS included (even if somehow missing)
if (!isset($clean_api_data['duration'])) {
    $clean_api_data['duration'] = $finalDuration;
    error_log("WARNING: Duration was missing from clean_api_data, adding it: $finalDuration");
}
// CRITICAL: Ensure instrumental is ALWAYS included (even if somehow missing)
if (!isset($clean_api_data['instrumental'])) {
    $clean_api_data['instrumental'] = $isInstrumentalBool;
    error_log("WARNING: Instrumental was missing from clean_api_data, adding it: " . ($isInstrumentalBool ? 'TRUE' : 'FALSE'));
}

// CRITICAL: Ensure title is NEVER over 80 characters (API requirement)
if (isset($clean_api_data['title'])) {
    $titleLength = function_exists('mb_strlen') ? mb_strlen($clean_api_data['title'], 'UTF-8') : strlen($clean_api_data['title']);
    if ($titleLength > 80) {
        $clean_api_data['title'] = function_exists('mb_substr') ? mb_substr($clean_api_data['title'], 0, 80, 'UTF-8') : substr($clean_api_data['title'], 0, 80);
        error_log("CRITICAL: Title was over 80 characters ($titleLength), truncated to 80: '" . $clean_api_data['title'] . "'");
    }
    error_log("Final title being sent to API: '" . $clean_api_data['title'] . "' (length: " . (function_exists('mb_strlen') ? mb_strlen($clean_api_data['title'], 'UTF-8') : strlen($clean_api_data['title'])) . ")");
}

$api_data = $clean_api_data;
error_log("create_music.php: Final API data instrumental value: " . (isset($api_data['instrumental']) ? ($api_data['instrumental'] ? 'TRUE (boolean)' : 'FALSE (boolean)') : 'NOT SET'));

// Log critical information
error_log("=== API REQUEST SUMMARY ===");
error_log("Prompt: " . substr($finalPrompt, 0, 100) . (strlen($finalPrompt) > 100 ? '...' : ''));
error_log("Duration: $finalDuration seconds (" . round($finalDuration / 60, 1) . " minutes)");
error_log("Duration in api_data: " . (isset($api_data['duration']) ? $api_data['duration'] : 'NOT SET'));
error_log("CustomMode: " . ($customMode === 'true' || $customMode === true ? 'TRUE' : 'FALSE'));
error_log("Instrumental: " . ($isInstrumentalBool ? 'TRUE (boolean)' : 'FALSE (boolean)'));
error_log("Model: $finalModel");
error_log("Style: " . (isset($api_data['style']) ? $api_data['style'] : 'NOT SET'));
error_log("Title: " . (isset($api_data['title']) ? $api_data['title'] : 'NOT SET'));
error_log("Duration: " . (isset($api_data['duration']) ? $api_data['duration'] : 'NOT SET'));
error_log("===========================");

// Log the exact API data being sent (clean version)
$api_data_log = [
    'timestamp' => date('Y-m-d H:i:s'),
    'customMode' => $customMode,
    'customMode_sent' => $isCustomModeBool ? 'true (boolean)' : 'false (boolean)',
    'duration' => $finalDuration,
    'prompt_length' => $promptLength,
    'full_request' => $api_data
];
$api_data_log = "[" . date('Y-m-d H:i:s') . "] API Request Data:\n" . json_encode($api_data_log, JSON_PRETTY_PRINT) . "\n";
file_put_contents(__DIR__ . '/create_music_debug.log', $api_data_log, FILE_APPEND);
error_log("API Request - customMode: " . ($isCustomModeBool ? 'TRUE (boolean)' : 'FALSE (boolean)') . ", duration: $finalDuration, prompt_length: $promptLength");
error_log("API Request Data (CLEAN - only API.box supported params): " . json_encode($api_data));
error_log("API Request - Instrumental value in JSON: " . (isset($api_data['instrumental']) ? json_encode($api_data['instrumental']) : 'NOT SET'));

// NOTE: All unsupported parameters have been removed above.
// API.box ONLY accepts: prompt, model, style, title, customMode, instrumental, duration, callBackUrl
// Any other parameters will interfere with the API and degrade music quality.

$api_key = '63edba40620216c5aa2c04240ac41dbd';

// Encode API data with proper UTF-8 handling and preserve special characters
// Use JSON flags only if available (PHP version compatibility)
$json_flags = 0;
if (defined('JSON_UNESCAPED_UNICODE')) {
    $json_flags |= JSON_UNESCAPED_UNICODE;
}
if (defined('JSON_UNESCAPED_SLASHES')) {
    $json_flags |= JSON_UNESCAPED_SLASHES;
}
if (defined('JSON_PARTIAL_OUTPUT_ON_ERROR')) {
    $json_flags |= JSON_PARTIAL_OUTPUT_ON_ERROR;
}

try {
    $json_data = json_encode($api_data, $json_flags);
    if ($json_data === false) {
        $error_msg = function_exists('json_last_error_msg') ? json_last_error_msg() : 'Unknown JSON error (code: ' . json_last_error() . ')';
        error_log("JSON encoding error: " . $error_msg);
        error_log("API data that failed to encode: " . print_r($api_data, true));
        throw new Exception("JSON encoding failed: " . $error_msg);
    }
} catch (Exception $e) {
    error_log("JSON encoding exception: " . $e->getMessage());
    $_SESSION['error'] = 'Error encoding music parameters. Please try again with simpler text.';
    header('Location: index.php#create');
    exit;
}

// Log the encoded data for debugging (truncate if too long)
$log_data = strlen($json_data) > 1000 ? substr($json_data, 0, 1000) . '...' : $json_data;
error_log("API request URL: $api_url");
error_log("API request JSON (first 1000 chars): " . $log_data);
error_log("API request full data structure: " . print_r($api_data, true));

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $api_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $json_data);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $api_key,
    'Content-Type: application/json',
    'User-Agent: SoundStudioPro-Music/2.0'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30); // Total timeout
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); // Connection timeout
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_ENCODING, ''); // Accept any encoding

error_log("create_music.php: Starting cURL request to API");
error_log("create_music.php: API URL: $api_url");
error_log("create_music.php: Request timeout set to 30 seconds");

$start_time = microtime(true);
$response = curl_exec($ch);
$end_time = microtime(true);
$request_duration = round($end_time - $start_time, 2);

error_log("create_music.php: cURL request completed in {$request_duration} seconds");

$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
$curl_info = curl_getinfo($ch);
curl_close($ch);

error_log("create_music.php: HTTP Code: $http_code");
error_log("create_music.php: cURL Error: " . ($curl_error ?: 'None'));
error_log("create_music.php: Total time: " . ($curl_info['total_time'] ?? 'N/A') . " seconds");

// Log the API response
$api_response_log = "[" . date('Y-m-d H:i:s') . "] API Response HTTP Code: $http_code\n";
$api_response_log .= "API Response Body: " . $response . "\n";
error_log($api_response_log);
file_put_contents(__DIR__ . '/create_music_debug.log', $api_response_log, FILE_APPEND);

if ($curl_error) {
    $error_msg = "[" . date('Y-m-d H:i:s') . "] create_music.php: CURL Error: $curl_error\n";
    error_log($error_msg);
    file_put_contents(__DIR__ . '/create_music_debug.log', $error_msg, FILE_APPEND);
    $_SESSION['error'] = 'Failed to connect to music generation service. Please try again.';
    ob_end_clean();
    header('Location: index.php#create');
    exit;
}

if ($http_code !== 200) {
    $error_log_msg = "[" . date('Y-m-d H:i:s') . "] API returned non-200 status: $http_code, Response: $response\n";
    error_log($error_log_msg);
    file_put_contents(__DIR__ . '/create_music_debug.log', $error_log_msg, FILE_APPEND);
    $_SESSION['error'] = 'Music generation service returned an error (HTTP ' . $http_code . '). Please try again.';
    ob_end_clean();
    header('Location: index.php#create');
    exit;
}

$api_result = json_decode($response, true);

if (!$api_result) {
    error_log("Failed to decode API response as JSON");
    error_log("Raw response: " . $response);
    error_log("Response length: " . strlen($response));
    $_SESSION['error'] = 'Invalid response from music generation service. Please try again.';
    header('Location: index.php#create');
    exit;
}

// Log the full API response for debugging
error_log("API response decoded successfully: " . print_r($api_result, true));

// Check if API returned an error (check code field first - API returns HTTP 200 but code: 400 for errors)
if (isset($api_result['code']) && $api_result['code'] !== 200) {
    $error_msg = $api_result['msg'] ?? $api_result['message'] ?? $api_result['error'] ?? 'Unknown error from API';
    $api_error_code = $api_result['code'];
    
    // Sanitize error message - remove any references to API.Box or supplier names
    $error_msg = preg_replace('/\b(API\.Box|api\.box|API\.box|not found on API\.Box|not found in API\.Box|Task not found in API\.Box|Track.*not found on API\.Box)\b/i', '', $error_msg);
    $error_msg = trim($error_msg);
    if (empty($error_msg)) {
        $error_msg = 'Generation failed';
    }
    
    // Log user's local credits for debugging
    if (isset($_SESSION['user_id'])) {
        $stmt = $pdo->prepare("SELECT credits FROM users WHERE id = ?");
        $stmt->execute([$_SESSION['user_id']]);
        $user = $stmt->fetch(PDO::FETCH_ASSOC);
        $user_credits = $user['credits'] ?? 0;
        error_log("API returned error code: $api_error_code, message: $error_msg. User has $user_credits credits in local database.");
    }
    
    // Log prompt content for debugging model errors (truncate if too long)
    $promptPreview = strlen($finalPrompt) > 500 ? substr($finalPrompt, 0, 500) . '...' : $finalPrompt;
    error_log("API error occurred with prompt (first 500 chars): " . $promptPreview);
    // Check if prompt contains suspicious patterns that might cause issues
    if (preg_match('/\.\/\.|\.\.\/\.\./', $finalPrompt)) {
        error_log("WARNING: Prompt contains path-like patterns (./. or ../) that might cause API issues");
    }
    
    // CRITICAL: Update track status to 'failed' and store error metadata
    $error_metadata = json_encode([
        'code' => $api_error_code,
        'msg' => $error_msg,
        'error_type' => ($api_error_code == 400) ? 'content_violation' : ($api_error_code == 429 ? 'service_unavailable' : 'generation_failed'),
        'data' => $api_result,
        'timestamp' => date('Y-m-d H:i:s'),
        'detected_via' => 'initial_api_response'
    ]);
    
    try {
        $update_stmt = $pdo->prepare("
            UPDATE music_tracks 
            SET status = 'failed', 
                metadata = ?, 
                updated_at = NOW() 
            WHERE id = ?
        ");
        $update_stmt->execute([$error_metadata, $track_id]);
        error_log("✅ Track $track_id marked as FAILED due to API error code $api_error_code: $error_msg");
    } catch (Exception $e) {
        error_log("❌ Failed to update track $track_id to failed status: " . $e->getMessage());
    }
    
    // Special handling for 429 (insufficient credits on API side)
    if ($api_error_code == 429) {
        // This is an API-side credit issue, not a user account issue
        // The API key doesn't have enough credits, but the user's account might be fine
        error_log("API returned 429 (insufficient credits on API provider side). This is a system-level issue, not a user account issue.");
        
        // Get user's current credits to show in the message (if available)
        if (isset($user_credits) && is_numeric($user_credits)) {
            // Use translation with credits parameter
            $_SESSION['error'] = t('error.api.service_unavailable.message', ['credits' => $user_credits]);
        } else {
            // Use translation without credits parameter
            $_SESSION['error'] = t('error.api.service_unavailable.message_no_credits');
        }
        $_SESSION['error_allow_html'] = true; // Allow HTML formatting
    } else {
        // For other errors, show the API's exact message (sanitized of API.Box references only)
        $user_friendly_error = preg_replace('/\b(API\.Box|api\.box|API\.box|not found on API\.Box|not found in API\.Box|Task not found in API\.Box|Track.*not found on API\.Box)\b/i', '', $error_msg);
        $user_friendly_error = trim($user_friendly_error);
        $user_friendly_error = preg_replace('/\s+/', ' ', $user_friendly_error);
        
        if (empty($user_friendly_error)) {
            $user_friendly_error = 'Track generation failed';
        }
        
        error_log("API returned error code: $api_error_code, message: $error_msg");
        $_SESSION['error'] = htmlspecialchars($user_friendly_error) . ' Your track has been marked as failed.';
    }
    
    ob_end_clean();
    header('Location: index.php#create');
    exit;
}

// Check if API returned an error message in other formats
$api_message = $api_result['message'] ?? $api_result['msg'] ?? '';
$has_error = isset($api_result['error']) || (isset($api_result['message']) && stripos($api_message, 'error') !== false);
if ($has_error) {
    $error_msg = $api_result['error'] ?? $api_result['message'] ?? $api_result['msg'] ?? 'Unknown error';
    
    // Sanitize error message
    $error_msg = preg_replace('/\b(|||not found|not found|Task not found|Track.*not found)\b/i', '', $error_msg);
    $error_msg = trim($error_msg);
    if (empty($error_msg)) {
        $error_msg = 'Generation failed';
    }
    
    error_log("API returned error: " . $error_msg);
    
    // CRITICAL: Update track status to 'failed' and store error metadata
    $error_metadata = json_encode([
        'code' => 531,
        'msg' => $error_msg,
        'error_type' => 'generation_failed',
        'data' => $api_result,
        'timestamp' => date('Y-m-d H:i:s'),
        'detected_via' => 'initial_api_response_other_format'
    ]);
    
    try {
        $update_stmt = $pdo->prepare("
            UPDATE music_tracks 
            SET status = 'failed', 
                metadata = ?, 
                updated_at = NOW() 
            WHERE id = ?
        ");
        $update_stmt->execute([$error_metadata, $track_id]);
        error_log("✅ Track $track_id marked as FAILED due to API error: $error_msg");
    } catch (Exception $e) {
        error_log("❌ Failed to update track $track_id to failed status: " . $e->getMessage());
    }
    
    // Sanitize error message - remove API.Box references only
    $user_friendly_error = preg_replace('/\b(API\.Box|api\.box|API\.box|not found on API\.Box|not found in API\.Box|Task not found in API\.Box|Track.*not found on API\.Box)\b/i', '', $error_msg);
    $user_friendly_error = trim($user_friendly_error);
    $user_friendly_error = preg_replace('/\s+/', ' ', $user_friendly_error);
    
    if (empty($user_friendly_error)) {
        $user_friendly_error = 'Track generation failed';
    }
    
    $_SESSION['error'] = htmlspecialchars($user_friendly_error) . ' Your track has been marked as failed.';
    ob_end_clean();
    header('Location: index.php#create');
    exit;
}

// Extract task ID from response - check multiple possible locations
$real_task_id = null;
if (isset($api_result['taskId'])) {
    $real_task_id = $api_result['taskId'];
} elseif (isset($api_result['id'])) {
    $real_task_id = $api_result['id'];
} elseif (isset($api_result['data']['taskId'])) {
    $real_task_id = $api_result['data']['taskId'];
} elseif (isset($api_result['task_id'])) {
    $real_task_id = $api_result['task_id'];
} elseif (isset($api_result['data']['id'])) {
    $real_task_id = $api_result['data']['id'];
} else {
    // CRITICAL: API didn't return a task ID - this means the request failed
    $error_log_msg = "[" . date('Y-m-d H:i:s') . "] CRITICAL ERROR: No task ID found in API response!\n";
    $error_log_msg .= "Temp task ID: $temp_task_id\n";
    $error_log_msg .= "API response structure: " . json_encode($api_result, JSON_PRETTY_PRINT) . "\n";
    $error_log_msg .= "Full raw response: " . $response . "\n";
    error_log($error_log_msg);
    file_put_contents(__DIR__ . '/create_music_debug.log', $error_log_msg, FILE_APPEND);
    
    $_SESSION['error'] = 'API did not return a task ID. The music generation request may have failed. Please try again.';
    ob_end_clean();
    header('Location: index.php#create');
    exit;
}

error_log("API call successful. Task ID: $real_task_id, Track ID: $track_id");

// CRITICAL: Only deduct credits AFTER successful API response
// This prevents losing credits if API call fails
if ($shouldDeductCredits) {
    // User is on credit system - deduct credits NOW (after API success)
    $stmt = $pdo->prepare("SELECT credits FROM users WHERE id = ?");
    $stmt->execute([$_SESSION['user_id']]);
    $user = $stmt->fetch(PDO::FETCH_ASSOC);
    
    if ($user && (int)$user['credits'] >= $creditCost) {
        $newCredits = (int)$user['credits'] - $creditCost;
        $stmt = $pdo->prepare("UPDATE users SET credits = ? WHERE id = ?");
        $stmt->execute([$newCredits, $_SESSION['user_id']]);
        
        // Record credit transaction
        // Determine mode based on form fields (all modes cost 1 credit now)
        $modeDescription = 'Simple Mode'; // Default
        if (!empty($_POST['proPrompt']) || !empty($_POST['proModel']) || isset($_POST['proDuration'])) {
            $modeDescription = 'Pro Mode';
        } elseif (!empty($_POST['advancedPrompt']) || !empty($_POST['title']) || isset($_POST['variations'])) {
            $modeDescription = 'Advanced Mode';
        }
        
        $stmt = $pdo->prepare("
            INSERT INTO credit_transactions (user_id, amount, type, description, created_at) 
            VALUES (?, ?, 'usage', 'Music track creation ($modeDescription): $finalTitle', NOW())
        ");
        $stmt->execute([$_SESSION['user_id'], -$creditCost]);
        
        $_SESSION['credits'] = $newCredits;
        error_log("create_music.php: Deducted 1 credit AFTER API success, new balance: $newCredits");
    } else {
        error_log("create_music.php: WARNING - Credits insufficient after API success (race condition?)");
        // This shouldn't happen, but if it does, we need to handle it
        // The track was already created, so we can't rollback
        // Log for investigation
    }
} elseif ($shouldIncrementUsage) {
    // User is on subscription - increment monthly usage AFTER API success
    error_log("create_music.php: Attempting to increment monthly track usage for user {$_SESSION['user_id']}");
    $increment_result = incrementMonthlyTrackUsage($_SESSION['user_id']);
    if ($increment_result) {
        error_log("create_music.php: Successfully incremented monthly track usage AFTER API success");
    } else {
        error_log("create_music.php: WARNING - Failed to increment monthly track usage for user {$_SESSION['user_id']}. Check subscription and usage record.");
    }
} else {
    error_log("create_music.php: NOT incrementing usage - shouldIncrementUsage=false, shouldDeductCredits=" . ($shouldDeductCredits ? 'true' : 'false') . ", track_check system: " . ($track_check['system'] ?? 'not set'));
}

// Update track with real task ID
try {
    $stmt = $pdo->prepare("UPDATE music_tracks SET task_id = ? WHERE id = ?");
    $stmt->execute([$real_task_id, $track_id]);
    error_log("Track updated with task ID: $real_task_id");
} catch (Exception $e) {
    error_log("Error updating track with task ID: " . $e->getMessage());
    // Don't fail the whole request if this update fails
}

$_SESSION['success'] = t('success.music_generation.started');

$success_msg = "[" . date('Y-m-d H:i:s') . "] create_music.php: Script completed successfully, redirecting to index.php, Track ID: $track_id, Task ID: $real_task_id\n";
error_log($success_msg);
file_put_contents(__DIR__ . '/create_music_debug.log', $success_msg, FILE_APPEND);

// Clear output buffer and redirect
ob_end_clean();
header('Location: index.php#create');
exit;
?> 

CasperSecurity Mini