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/public_html/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/soundstudiopro.com/public_html/api_social.php
<?php
// Configure session before starting it
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 0); // Set to 1 if using HTTPS
ini_set('session.cookie_samesite', 'Lax');
ini_set('session.use_strict_mode', 1);
ini_set('session.use_cookies', 1);
ini_set('session.use_only_cookies', 1);

session_start();

// Ensure session cookie is set
if (!isset($_COOKIE[session_name()])) {
    setcookie(session_name(), session_id(), time() + 3600, '/', '', false, true);
}

require_once 'config/database.php';

header('Content-Type: application/json');

// Check if user is logged in for certain actions
$user_id = $_SESSION['user_id'] ?? null;

// Get the request data
$input = json_decode(file_get_contents('php://input'), true);
$action = $input['action'] ?? $_POST['action'] ?? $_GET['action'] ?? '';
$track_id = $input['track_id'] ?? $_POST['track_id'] ?? $_GET['track_id'] ?? null;
$user_id_param = $input['user_id'] ?? $_POST['user_id'] ?? $_GET['user_id'] ?? null;

$pdo = getDBConnection();
if (!$pdo) {
    echo json_encode(['success' => false, 'message' => 'Database connection failed']);
    exit;
}

/**
 * Ensure the rating constraint for a table allows values 1-10.
 */
function ensureRatingConstraint(PDO $pdo, string $tableName, string $constraintName = ''): void {
    $constraintName = $constraintName ?: $tableName . '_chk_rating';

    // Collect existing constraint names from SHOW CREATE TABLE (covers MySQL & MariaDB)
    $existingConstraints = [];
    try {
        $stmt = $pdo->query("SHOW CREATE TABLE `{$tableName}`");
        $row = $stmt ? $stmt->fetch(PDO::FETCH_NUM) : null;
        if ($row && isset($row[1])) {
            $createSql = $row[1];
            if (preg_match_all('/CONSTRAINT `([^`]+)`\s+CHECK/i', $createSql, $matches)) {
                $existingConstraints = $matches[1];
            }
        }
    } catch (PDOException $e) {
        error_log("❗ Unable to inspect existing constraints for {$tableName}: " . $e->getMessage());
    }

    $possibleNames = array_unique(array_merge(
        $existingConstraints,
        [
            $constraintName,
            "{$tableName}.rating",
            "{$tableName}_rating_chk",
            "{$tableName}_rating_check"
        ]
    ));

    foreach ($possibleNames as $name) {
        if (!$name) {
            continue;
        }
        try {
            $pdo->exec("ALTER TABLE `{$tableName}` DROP CHECK `{$name}`");
        } catch (PDOException $e) {
            // Some engines (MariaDB) expect DROP CONSTRAINT instead of DROP CHECK
            try {
                $pdo->exec("ALTER TABLE `{$tableName}` DROP CONSTRAINT `{$name}`");
            } catch (PDOException $e2) {
                // ignore if still failing (maybe name doesn't exist)
            }
        }
    }

    // Ensure column type can store 1-10 without old constraints lingering
    try {
        $pdo->exec("ALTER TABLE `{$tableName}` MODIFY `rating` TINYINT UNSIGNED NOT NULL");
    } catch (PDOException $e) {
        error_log("❗ Unable to adjust rating column on {$tableName}: " . $e->getMessage());
    }

    // Recreate explicit check if supported (otherwise rely on application validation)
    try {
        $pdo->exec("ALTER TABLE `{$tableName}` ADD CONSTRAINT `{$constraintName}` CHECK (`rating` BETWEEN 1 AND 10)");
    } catch (PDOException $e) {
        // Some MySQL/MariaDB versions don't support CHECK; log but continue
        error_log("â„šī¸ Unable to add rating check on {$tableName}: " . $e->getMessage());
    }
}

/**
 * Determine if a PDOException was caused by a rating constraint failure.
 */
function isRatingConstraintError(PDOException $e, string $tableName): bool {
    if ($e->getCode() === '23000') {
        $message = $e->getMessage();
        if (stripos($message, $tableName . '.rating') !== false || stripos($message, 'rating') !== false) {
            return true;
        }
    }
    return false;
}

try {
    switch ($action) {
        case 'like':
            if (!$user_id) {
                echo json_encode(['success' => false, 'message' => 'Please log in to like tracks']);
                exit;
            }
            
            error_log("Like request - User ID: $user_id, Track ID: $track_id");
            
            // Check if track exists
            $stmt = $pdo->prepare("SELECT id FROM music_tracks WHERE id = ?");
            $stmt->execute([$track_id]);
            $track_exists = $stmt->fetch();
            
            if (!$track_exists) {
                error_log("Track not found: $track_id");
                echo json_encode(['success' => false, 'message' => 'Track not found']);
                exit;
            }
            
            // Check if already liked
            $stmt = $pdo->prepare("SELECT id FROM track_likes WHERE track_id = ? AND user_id = ?");
            $stmt->execute([$track_id, $user_id]);
            $existing_like = $stmt->fetch();
            
            if ($existing_like) {
                // Unlike
                try {
                    $stmt = $pdo->prepare("DELETE FROM track_likes WHERE track_id = ? AND user_id = ?");
                    $stmt->execute([$track_id, $user_id]);
                    error_log("Track unliked successfully - User: $user_id, Track: $track_id");
                    echo json_encode(['success' => true, 'action' => 'unliked']);
                } catch (Exception $e) {
                    error_log("Unlike error: " . $e->getMessage());
                    echo json_encode(['success' => false, 'message' => 'Failed to unlike track']);
                }
            } else {
                // Like
                try {
                    $stmt = $pdo->prepare("INSERT INTO track_likes (track_id, user_id) VALUES (?, ?)");
                    $stmt->execute([$track_id, $user_id]);
                    error_log("Track liked successfully - User: $user_id, Track: $track_id");
                    echo json_encode(['success' => true, 'action' => 'liked']);
                } catch (Exception $e) {
                    error_log("Like error: " . $e->getMessage());
                    echo json_encode(['success' => false, 'message' => 'Failed to like track: ' . $e->getMessage()]);
                }
            }
            break;
            
        case 'view':
            // Track view (can be anonymous)
            $ip_address = $_SERVER['REMOTE_ADDR'] ?? '';
            $stmt = $pdo->prepare("INSERT INTO track_views (track_id, user_id, ip_address) VALUES (?, ?, ?)");
            $stmt->execute([$track_id, $user_id, $ip_address]);
            echo json_encode(['success' => true]);
            break;
            
        case 'play':
            // Track play (can be anonymous)
            $stmt = $pdo->prepare("INSERT INTO track_plays (track_id, user_id) VALUES (?, ?)");
            $stmt->execute([$track_id, $user_id]);
            echo json_encode(['success' => true]);
            break;
            
        case 'vote_track':
            try {
                if (!$user_id) {
                    echo json_encode(['success' => false, 'message' => 'Please log in to vote on tracks']);
                    exit;
                }
                
                if (!$track_id) {
                    echo json_encode(['success' => false, 'message' => 'Track ID is required']);
                    exit;
                }
                
                $vote_type = $input['vote_type'] ?? 'up';
                
                // Validate vote_type
                if (!in_array($vote_type, ['up', 'down'])) {
                    echo json_encode(['success' => false, 'message' => 'Invalid vote type']);
                    exit;
                }
                
                // Check if track exists
                $stmt = $pdo->prepare("SELECT id FROM music_tracks WHERE id = ?");
                $stmt->execute([$track_id]);
                $track_exists = $stmt->fetch();
                
                if (!$track_exists) {
                    echo json_encode(['success' => false, 'message' => 'Track not found']);
                    exit;
                }
                
                // Check if already voted
                $stmt = $pdo->prepare("SELECT id, vote_type FROM track_votes WHERE track_id = ? AND user_id = ?");
                $stmt->execute([$track_id, $user_id]);
                $existing_vote = $stmt->fetch();
                
                if ($existing_vote) {
                    if ($existing_vote['vote_type'] === $vote_type) {
                        // Remove vote (same vote type clicked again)
                        $stmt = $pdo->prepare("DELETE FROM track_votes WHERE track_id = ? AND user_id = ?");
                        $stmt->execute([$track_id, $user_id]);
                        $action = 'vote_removed';
                    } else {
                        // Change vote type
                        $stmt = $pdo->prepare("UPDATE track_votes SET vote_type = ? WHERE track_id = ? AND user_id = ?");
                        $stmt->execute([$vote_type, $track_id, $user_id]);
                        $action = 'vote_changed';
                    }
                } else {
                    // Add new vote
                    $stmt = $pdo->prepare("INSERT INTO track_votes (track_id, user_id, vote_type) VALUES (?, ?, ?)");
                    $stmt->execute([$track_id, $user_id, $vote_type]);
                    $action = 'vote_added';
                }
                
                // Get updated vote count (net votes: up - down) - same as track.php
                $vote_count_stmt = $pdo->prepare("
                    SELECT COALESCE(SUM(CASE WHEN vote_type = 'up' THEN 1 ELSE -1 END), 0) as vote_count
                    FROM track_votes 
                    WHERE track_id = ?
                ");
                $vote_count_stmt->execute([$track_id]);
                $vote_result = $vote_count_stmt->fetch(PDO::FETCH_ASSOC);
                $vote_count = (int)($vote_result['vote_count'] ?? 0);
                
                // Get user's current vote
                $user_vote_stmt = $pdo->prepare("SELECT vote_type FROM track_votes WHERE track_id = ? AND user_id = ?");
                $user_vote_stmt->execute([$track_id, $user_id]);
                $user_vote_result = $user_vote_stmt->fetch(PDO::FETCH_ASSOC);
                $user_vote = $user_vote_result ? $user_vote_result['vote_type'] : null;
                
                // Debug logging
                error_log("Vote API Response: track_id=$track_id, action=$action, vote_count=$vote_count, user_vote=" . ($user_vote ?? 'null'));
                
                echo json_encode([
                    'success' => true, 
                    'action' => $action,
                    'vote_count' => $vote_count,
                    'user_vote' => $user_vote
                ]);
            } catch (PDOException $e) {
                error_log("Vote track database error: " . $e->getMessage());
                error_log("Vote track SQL error code: " . $e->getCode());
                error_log("Vote track error info: " . print_r($e->errorInfo ?? [], true));
                // Show more detailed error in development
                $error_msg = (defined('DEBUG_MODE') && DEBUG_MODE) 
                    ? 'Database error: ' . $e->getMessage() . ' (Code: ' . $e->getCode() . ')'
                    : 'Database error while processing vote. Please try again.';
                echo json_encode([
                    'success' => false, 
                    'message' => $error_msg
                ]);
            } catch (Exception $e) {
                error_log("Vote track error: " . $e->getMessage());
                error_log("Vote track error trace: " . $e->getTraceAsString());
                $error_msg = (defined('DEBUG_MODE') && DEBUG_MODE) 
                    ? 'Error: ' . $e->getMessage()
                    : 'An error occurred while processing your vote. Please try again.';
                echo json_encode([
                    'success' => false, 
                    'message' => $error_msg
                ]);
            }
            break;
            
        case 'share':
            // Track share (can be anonymous)
            $platform = $input['platform'] ?? 'unknown';
            $share_type = $input['share_type'] ?? $platform;
            
            // Create the share record
            $stmt = $pdo->prepare("INSERT INTO track_shares (track_id, user_id, share_type, platform, created_at) VALUES (?, ?, ?, ?, NOW())");
            $stmt->execute([$track_id, $user_id, $share_type, $platform]);
            
            // Get updated share count
            $stmt = $pdo->prepare("SELECT COUNT(*) as share_count FROM track_shares WHERE track_id = ?");
            $stmt->execute([$track_id]);
            $result = $stmt->fetch(PDO::FETCH_ASSOC);
            $share_count = $result['share_count'] ?? 0;
            
            // Log the share for analytics
            error_log("🚀 Track shared: Track ID $track_id via $platform by user " . ($user_id ?: 'anonymous'));
            
            echo json_encode([
                'success' => true, 
                'message' => "Track shared via $platform!",
                'platform' => $platform,
                'share_count' => (int)$share_count
            ]);
            break;
            
        case 'follow':
            if (!$user_id) {
                echo json_encode(['success' => false, 'message' => 'Please log in to follow users']);
                exit;
            }
            
            if ($user_id == $user_id_param) {
                echo json_encode(['success' => false, 'message' => 'You cannot follow yourself']);
                exit;
            }
            
            // Check if already following
            $stmt = $pdo->prepare("SELECT id FROM user_follows WHERE follower_id = ? AND following_id = ?");
            $stmt->execute([$user_id, $user_id_param]);
            $existing_follow = $stmt->fetch();
            
            if ($existing_follow) {
                // Unfollow
                $stmt = $pdo->prepare("DELETE FROM user_follows WHERE follower_id = ? AND following_id = ?");
                $stmt->execute([$user_id, $user_id_param]);
                echo json_encode(['success' => true, 'action' => 'unfollowed']);
            } else {
                // Follow
                $stmt = $pdo->prepare("INSERT INTO user_follows (follower_id, following_id) VALUES (?, ?)");
                $stmt->execute([$user_id, $user_id_param]);
                $follow_id = $pdo->lastInsertId();
                
                // Create notification for the followed user
                try {
                    // Ensure notification_reads table exists with 'follow' type
                    $pdo->exec("
                        CREATE TABLE IF NOT EXISTS notification_reads (
                            id INT AUTO_INCREMENT PRIMARY KEY,
                            user_id INT NOT NULL,
                            notification_type ENUM('friend_request', 'like', 'comment', 'artist_rating', 'track_rating', 'follow') NOT NULL,
                            notification_id INT NOT NULL,
                            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                            UNIQUE KEY unique_notification (user_id, notification_type, notification_id),
                            FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
                            INDEX idx_user_type (user_id, notification_type)
                        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
                    ");
                    
                    // Try to alter the ENUM if table already exists
                    try {
                        $pdo->exec("ALTER TABLE notification_reads MODIFY notification_type ENUM('friend_request', 'like', 'comment', 'artist_rating', 'track_rating', 'follow') NOT NULL");
                    } catch (PDOException $e) {
                        // ENUM might already include 'follow' or alter might fail, that's okay
                    }
                } catch (PDOException $e) {
                    // Table creation might fail if it exists, that's okay
                }
                
                echo json_encode(['success' => true, 'action' => 'followed']);
            }
            break;
            
        case 'comment':
            if (!$user_id) {
                echo json_encode(['success' => false, 'message' => 'Please log in to comment']);
                exit;
            }
            
            $comment = $input['comment'] ?? '';
            if (empty($comment)) {
                echo json_encode(['success' => false, 'message' => 'Comment cannot be empty']);
                exit;
            }
            
            $stmt = $pdo->prepare("INSERT INTO track_comments (track_id, user_id, comment) VALUES (?, ?, ?)");
            $stmt->execute([$track_id, $user_id, $comment]);
            echo json_encode(['success' => true, 'message' => 'Comment added successfully']);
            break;
            
        case 'get_comments':
            $track_id = $_GET['track_id'] ?? null;
            $page = (int)($_GET['page'] ?? 1);
            $limit = (int)($_GET['limit'] ?? 20);
            
            if (!$track_id) {
                echo json_encode(['success' => false, 'message' => 'Track ID required']);
                exit;
            }
            
            // Validate pagination parameters
            if ($page < 1) $page = 1;
            if ($limit < 1 || $limit > 100) $limit = 20;
            
            $offset = ($page - 1) * $limit;
            
            // Get total count first
            $countStmt = $pdo->prepare("SELECT COUNT(*) as total FROM track_comments WHERE track_id = ?");
            $countStmt->execute([$track_id]);
            $totalComments = $countStmt->fetch()['total'];
            
            // Get paginated comments
            $stmt = $pdo->prepare("
                SELECT tc.*, u.name as user_name, u.profile_image 
                FROM track_comments tc 
                JOIN users u ON tc.user_id = u.id 
                WHERE tc.track_id = ? 
                ORDER BY tc.created_at DESC
                LIMIT ? OFFSET ?
            ");
            $stmt->execute([$track_id, $limit, $offset]);
            $comments = $stmt->fetchAll();
            
            // Calculate pagination info
            $totalPages = ceil($totalComments / $limit);
            
            echo json_encode([
                'success' => true, 
                'comments' => $comments,
                'pagination' => [
                    'current_page' => $page,
                    'total_pages' => $totalPages,
                    'total_comments' => $totalComments,
                    'per_page' => $limit,
                    'has_next' => $page < $totalPages,
                    'has_prev' => $page > 1
                ]
            ]);
            break;
            
        case 'friend_request':
            if (!$user_id) {
                echo json_encode(['success' => false, 'message' => 'Please log in to send friend requests']);
                exit;
            }
            
            $friend_id = $input['friend_id'] ?? null;
            if (!$friend_id || $user_id == $friend_id) {
                echo json_encode(['success' => false, 'message' => 'Invalid friend request']);
                exit;
            }
            
            // Check if friendship already exists
            $stmt = $pdo->prepare("SELECT id, status FROM user_friends WHERE (user_id = ? AND friend_id = ?) OR (user_id = ? AND friend_id = ?)");
            $stmt->execute([$user_id, $friend_id, $friend_id, $user_id]);
            $existing_friendship = $stmt->fetch();
            
            if ($existing_friendship) {
                if ($existing_friendship['status'] === 'accepted') {
                    // Remove friendship
                    $stmt = $pdo->prepare("DELETE FROM user_friends WHERE id = ?");
                    $stmt->execute([$existing_friendship['id']]);
                    echo json_encode(['success' => true, 'action' => 'unfriended']);
                } else {
                    echo json_encode(['success' => false, 'message' => 'Friend request already sent']);
                }
            } else {
                // Send friend request
                $stmt = $pdo->prepare("INSERT INTO user_friends (user_id, friend_id, status) VALUES (?, ?, 'pending')");
                $stmt->execute([$user_id, $friend_id]);
                echo json_encode(['success' => true, 'action' => 'friend_request_sent']);
            }
            break;
            
        case 'accept_friend':
            if (!$user_id) {
                echo json_encode(['success' => false, 'message' => 'Please log in to accept friend requests']);
                exit;
            }
            
            $friend_id = $input['friend_id'] ?? null;
            if (!$friend_id) {
                echo json_encode(['success' => false, 'message' => 'Invalid friend ID']);
                exit;
            }
            
            // Update friend request status
            $stmt = $pdo->prepare("UPDATE user_friends SET status = 'accepted' WHERE friend_id = ? AND user_id = ? AND status = 'pending'");
            $stmt->execute([$user_id, $friend_id]);
            
            if ($stmt->rowCount() > 0) {
                echo json_encode(['success' => true, 'action' => 'friend_accepted']);
            } else {
                echo json_encode(['success' => false, 'message' => 'No pending friend request found']);
            }
            break;
            
        case 'decline_friend':
            if (!$user_id) {
                echo json_encode(['success' => false, 'message' => 'Please log in to decline friend requests']);
                exit;
            }
            
            $friend_id = $input['friend_id'] ?? null;
            if (!$friend_id) {
                echo json_encode(['success' => false, 'message' => 'Invalid friend ID']);
                exit;
            }
            
            // Delete friend request
            $stmt = $pdo->prepare("DELETE FROM user_friends WHERE friend_id = ? AND user_id = ? AND status = 'pending'");
            $stmt->execute([$user_id, $friend_id]);
            
            if ($stmt->rowCount() > 0) {
                echo json_encode(['success' => true, 'action' => 'friend_declined']);
            } else {
                echo json_encode(['success' => false, 'message' => 'No pending friend request found']);
            }
            break;
            
        case 'send_message':
            if (!$user_id) {
                echo json_encode(['success' => false, 'message' => 'Please log in to send messages']);
                exit;
            }
            
            $receiver_id = $input['receiver_id'] ?? null;
            $message = $input['message'] ?? '';
            
            if (!$receiver_id || empty($message)) {
                echo json_encode(['success' => false, 'message' => 'Receiver ID and message are required']);
                exit;
            }
            
            if ($user_id == $receiver_id) {
                echo json_encode(['success' => false, 'message' => 'You cannot send messages to yourself']);
                exit;
            }
            
            // Insert message
            $stmt = $pdo->prepare("INSERT INTO user_messages (sender_id, receiver_id, message) VALUES (?, ?, ?)");
            $stmt->execute([$user_id, $receiver_id, $message]);
            
            echo json_encode(['success' => true, 'message' => 'Message sent successfully']);
            break;
            
        case 'get_messages':
            if (!$user_id) {
                echo json_encode(['success' => false, 'message' => 'Please log in to view messages']);
                exit;
            }
            
            $other_user_id = $_GET['user_id'] ?? null;
            if (!$other_user_id) {
                echo json_encode(['success' => false, 'message' => 'User ID required']);
                exit;
            }
            
            // Get messages between two users
            $stmt = $pdo->prepare("
                SELECT um.*, u.name as sender_name 
                FROM user_messages um 
                JOIN users u ON um.sender_id = u.id 
                WHERE (um.sender_id = ? AND um.receiver_id = ?) OR (um.sender_id = ? AND um.receiver_id = ?)
                ORDER BY um.created_at ASC
            ");
            $stmt->execute([$user_id, $other_user_id, $other_user_id, $user_id]);
            $messages = $stmt->fetchAll();
            
            // Mark messages as read
            $stmt = $pdo->prepare("UPDATE user_messages SET is_read = TRUE WHERE sender_id = ? AND receiver_id = ? AND is_read = FALSE");
            $stmt->execute([$other_user_id, $user_id]);
            
            echo json_encode(['success' => true, 'messages' => $messages]);
            break;
            
        case 'upload_profile_image':
            error_log("đŸŽĩ Profile image upload requested for user: " . ($user_id ?? 'not logged in'));
            
            if (!$user_id) {
                echo json_encode(['success' => false, 'message' => 'Please log in to upload images']);
                exit;
            }
            
            // Handle file upload
            if (!isset($_FILES['profile_image']) || $_FILES['profile_image']['error'] !== UPLOAD_ERR_OK) {
                error_log("đŸŽĩ Profile image upload error: " . ($_FILES['profile_image']['error'] ?? 'no file'));
                echo json_encode(['success' => false, 'message' => 'No image file uploaded or upload error']);
                exit;
            }
            
            $file = $_FILES['profile_image'];
            
            // SECURITY: Enhanced file upload validation
            require_once __DIR__ . '/includes/security.php';
            $validation = validateFileUpload($file, ['jpg', 'jpeg', 'png', 'gif', 'webp'], 5 * 1024 * 1024); // 5MB max
            
            if (!$validation['valid']) {
                error_log("SECURITY: Invalid profile image upload attempt from user $user_id: " . ($validation['error'] ?? 'unknown error'));
                echo json_encode(['success' => false, 'message' => 'Invalid file. ' . ($validation['error'] ?? 'Please upload a valid image file.')]);
                exit;
            }
            
            // Create uploads directory if it doesn't exist
            $upload_dir = 'uploads/profile_images/';
            if (!is_dir($upload_dir)) {
                mkdir($upload_dir, 0755, true);
            }
            
            // Use sanitized filename from validation
            $filename = 'profile_' . $user_id . '_' . time() . '_' . $validation['filename'];
            $filepath = $upload_dir . $filename;
            $db_path = '/' . $filepath; // Add leading slash for database
            
            if (move_uploaded_file($file['tmp_name'], $filepath)) {
                // Compress and optimize the image
                require_once __DIR__ . '/utils/image_compression.php';
                $compression_result = compressProfileImage($filepath);
                
                if ($compression_result['success']) {
                    // Update filename if format changed (e.g., PNG to JPEG)
                    if (!empty($compression_result['format_changed']) && $compression_result['format_changed']) {
                        $filename = basename($compression_result['filepath']);
                        $filepath = $upload_dir . $filename;
                        $db_path = '/' . $filepath;
                    }
                    error_log("đŸŽĩ Profile image compressed: " . ($compression_result['message'] ?? 'Success'));
                } else {
                    error_log("đŸŽĩ Image compression warning: " . ($compression_result['error'] ?? 'Unknown error'));
                    // Continue even if compression fails - original image is still usable
                }
                
                // Ensure user_profiles record exists
                $stmt = $pdo->prepare("SELECT COUNT(*) FROM user_profiles WHERE user_id = ?");
                $stmt->execute([$user_id]);
                if ($stmt->fetchColumn() == 0) {
                    // Create user_profiles record if it doesn't exist
                    $stmt = $pdo->prepare("INSERT INTO user_profiles (user_id, created_at, updated_at) VALUES (?, NOW(), NOW())");
                    $stmt->execute([$user_id]);
                    error_log("đŸŽĩ Created user_profiles record for user: " . $user_id);
                }
                
                // Update user profile in database
                $stmt = $pdo->prepare("UPDATE user_profiles SET profile_image = ? WHERE user_id = ?");
                $stmt->execute([$db_path, $user_id]);
                
                error_log("đŸŽĩ Profile image uploaded successfully: " . $filepath);
                echo json_encode(['success' => true, 'data' => ['image_url' => $db_path]]);
            } else {
                error_log("đŸŽĩ Profile image upload failed to move file");
                echo json_encode(['success' => false, 'message' => 'Failed to save image file']);
            }
            break;
            
        case 'upload_cover_image':
            error_log("đŸŽĩ Cover image upload requested for user: " . ($user_id ?? 'not logged in'));
            
            if (!$user_id) {
                echo json_encode(['success' => false, 'message' => 'Please log in to upload images']);
                exit;
            }
            
            // Ensure user_profiles record exists
            $stmt = $pdo->prepare("SELECT COUNT(*) FROM user_profiles WHERE user_id = ?");
            $stmt->execute([$user_id]);
            if ($stmt->fetchColumn() == 0) {
                // Create user_profiles record if it doesn't exist
                $stmt = $pdo->prepare("INSERT INTO user_profiles (user_id, created_at, updated_at) VALUES (?, NOW(), NOW())");
                $stmt->execute([$user_id]);
                error_log("đŸŽĩ Created user_profiles record for user: " . $user_id);
            }
            
            // Handle file upload
            if (!isset($_FILES['cover_image']) || $_FILES['cover_image']['error'] !== UPLOAD_ERR_OK) {
                error_log("đŸŽĩ Cover image upload error: " . ($_FILES['cover_image']['error'] ?? 'no file'));
                echo json_encode(['success' => false, 'message' => 'No image file uploaded or upload error']);
                exit;
            }
            
            $file = $_FILES['cover_image'];
            $allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
            
            if (!in_array($file['type'], $allowed_types)) {
                echo json_encode(['success' => false, 'message' => 'Invalid file type. Only JPEG, PNG, GIF, and WebP are allowed']);
                exit;
            }
            
            if ($file['size'] > 5 * 1024 * 1024) { // 5MB limit
                echo json_encode(['success' => false, 'message' => 'File size too large. Maximum 5MB allowed']);
                exit;
            }
            
            // Create uploads directory if it doesn't exist
            // SECURITY: Enhanced file upload validation
            require_once __DIR__ . '/includes/security.php';
            $validation = validateFileUpload($file, ['jpg', 'jpeg', 'png', 'gif', 'webp'], 5 * 1024 * 1024); // 5MB max
            
            if (!$validation['valid']) {
                error_log("SECURITY: Invalid cover image upload attempt from user $user_id: " . ($validation['error'] ?? 'unknown error'));
                echo json_encode(['success' => false, 'message' => 'Invalid file. ' . ($validation['error'] ?? 'Please upload a valid image file.')]);
                exit;
            }
            
            $upload_dir = 'uploads/cover_images/';
            if (!is_dir($upload_dir)) {
                mkdir($upload_dir, 0755, true);
            }
            
            // Use sanitized filename from validation
            $filename = 'cover_' . $user_id . '_' . time() . '_' . $validation['filename'];
            $filepath = $upload_dir . $filename;
            $db_path = '/' . $filepath; // Add leading slash for database
            
            if (move_uploaded_file($file['tmp_name'], $filepath)) {
                // Compress and optimize the image (cover images can be larger)
                require_once __DIR__ . '/utils/image_compression.php';
                $compression_result = compressImage($filepath, [
                    'max_width' => 1920,
                    'max_height' => 1080,
                    'quality' => 85,
                    'convert_png_to_jpeg' => true,
                    'max_file_size' => 500 * 1024, // 500KB max
                ]);
                
                if ($compression_result['success']) {
                    // Update filename if format changed (e.g., PNG to JPEG)
                    if (!empty($compression_result['format_changed']) && $compression_result['format_changed']) {
                        $filename = basename($compression_result['filepath']);
                        $filepath = $upload_dir . $filename;
                        $db_path = '/' . $filepath;
                    }
                    error_log("đŸŽĩ Cover image compressed: " . ($compression_result['message'] ?? 'Success'));
                } else {
                    error_log("đŸŽĩ Image compression warning: " . ($compression_result['error'] ?? 'Unknown error'));
                    // Continue even if compression fails - original image is still usable
                }
                
                // Update user profile in database
                $stmt = $pdo->prepare("UPDATE user_profiles SET cover_image = ? WHERE user_id = ?");
                $stmt->execute([$db_path, $user_id]);
                
                error_log("đŸŽĩ Cover image uploaded successfully: " . $filepath);
                echo json_encode(['success' => true, 'data' => ['image_url' => $db_path]]);
            } else {
                error_log("đŸŽĩ Cover image upload failed to move file");
                echo json_encode(['success' => false, 'message' => 'Failed to save image file']);
            }
            break;
            
        case 'save_cover_position':
            error_log("đŸŽĩ Cover position save requested for user: " . ($user_id ?? 'not logged in'));
            
            if (!$user_id) {
                echo json_encode(['success' => false, 'message' => 'Please log in to save cover position']);
                exit;
            }
            
            $position = trim($_POST['position'] ?? 'center center');
            $artist_id = (int)($_POST['artist_id'] ?? 0);
            
            // Validate position format (should be "center X%" where X is 0-100, allows decimals)
            if (!preg_match('/^center\s+\d+(?:\.\d+)?%$/', $position) && $position !== 'center center') {
                error_log("đŸŽĩ Invalid cover position format: " . $position);
                echo json_encode(['success' => false, 'message' => 'Invalid position format: ' . $position]);
                exit;
            }
            
            // Normalize "center center" to "center 50%"
            if ($position === 'center center') {
                $position = 'center 50%';
            }
            
            // Verify the user owns this profile
            if ($artist_id !== $user_id) {
                error_log("đŸŽĩ Security: User $user_id attempted to modify profile $artist_id");
                echo json_encode(['success' => false, 'message' => 'You can only modify your own profile']);
                exit;
            }
            
            try {
                // Ensure user_profiles record exists
                $stmt = $pdo->prepare("SELECT COUNT(*) FROM user_profiles WHERE user_id = ?");
                $stmt->execute([$user_id]);
                if ($stmt->fetchColumn() == 0) {
                    // Create user_profiles record if it doesn't exist
                    $stmt = $pdo->prepare("INSERT INTO user_profiles (user_id, created_at, updated_at) VALUES (?, NOW(), NOW())");
                    $stmt->execute([$user_id]);
                    error_log("đŸŽĩ Created user_profiles record for user: " . $user_id);
                }
                
                // Update cover position in database
                $stmt = $pdo->prepare("UPDATE user_profiles SET cover_position = ?, updated_at = NOW() WHERE user_id = ?");
                $result = $stmt->execute([$position, $user_id]);
                
                if (!$result) {
                    throw new Exception('Database update failed');
                }
                
                error_log("đŸŽĩ Cover position saved successfully: " . $position . " for user: " . $user_id);
                echo json_encode(['success' => true, 'data' => ['position' => $position]]);
            } catch (Exception $e) {
                error_log("đŸŽĩ Error saving cover position: " . $e->getMessage());
                echo json_encode(['success' => false, 'message' => 'Failed to save cover position. Please try again.']);
            }
            break;
            
        case 'save_profile_position':
            error_log("đŸŽĩ Profile position save requested for user: " . ($user_id ?? 'not logged in'));
            
            if (!$user_id) {
                echo json_encode(['success' => false, 'message' => 'Please log in to save profile position']);
                exit;
            }
            
            $position = $_POST['position'] ?? 'center center';
            $artist_id = (int)($_POST['artist_id'] ?? 0);
            
            // Verify the user owns this profile
            if ($artist_id !== $user_id) {
                echo json_encode(['success' => false, 'message' => 'You can only modify your own profile']);
                exit;
            }
            
            // Ensure user_profiles record exists
            $stmt = $pdo->prepare("SELECT COUNT(*) FROM user_profiles WHERE user_id = ?");
            $stmt->execute([$user_id]);
            if ($stmt->fetchColumn() == 0) {
                // Create user_profiles record if it doesn't exist
                $stmt = $pdo->prepare("INSERT INTO user_profiles (user_id, created_at, updated_at) VALUES (?, NOW(), NOW())");
                $stmt->execute([$user_id]);
                error_log("đŸŽĩ Created user_profiles record for user: " . $user_id);
            }
            
            // Update profile position in database
            $stmt = $pdo->prepare("UPDATE user_profiles SET profile_position = ? WHERE user_id = ?");
            $stmt->execute([$position, $user_id]);
            
            // Update session so header reflects the change immediately
            $_SESSION['profile_position'] = $position;
            
            error_log("đŸŽĩ Profile position saved successfully: " . $position);
            echo json_encode(['success' => true, 'data' => ['position' => $position]]);
            break;
            
        case 'update_profile':
            error_log("đŸŽĩ Profile update requested for user: " . ($user_id ?? 'not logged in'));
            
            if (!$user_id) {
                echo json_encode(['success' => false, 'message' => 'Please log in to update profile']);
                exit;
            }
            
            $field = $_POST['field'] ?? $input['field'] ?? '';
            $value = $_POST['value'] ?? $input['value'] ?? '';
            
            if (empty($field)) {
                echo json_encode(['success' => false, 'message' => 'Field name is required']);
                exit;
            }
            
            // Ensure user_profiles record exists
            $stmt = $pdo->prepare("SELECT COUNT(*) FROM user_profiles WHERE user_id = ?");
            $stmt->execute([$user_id]);
            if ($stmt->fetchColumn() == 0) {
                // Create user_profiles record if it doesn't exist
                $stmt = $pdo->prepare("INSERT INTO user_profiles (user_id, created_at, updated_at) VALUES (?, NOW(), NOW())");
                $stmt->execute([$user_id]);
                error_log("đŸŽĩ Created user_profiles record for user: " . $user_id);
            }
            
            // Map field names to database columns
            $field_mapping = [
                'highlights' => 'artist_highlights',
                'bio' => 'bio',
                'influences' => 'influences',
                'equipment' => 'equipment',
                'achievements' => 'achievements',
                'artist_statement' => 'artist_statement'
            ];
            
            $db_field = $field_mapping[$field] ?? $field;
            
            // Update profile field in database
            $stmt = $pdo->prepare("UPDATE user_profiles SET $db_field = ?, updated_at = NOW() WHERE user_id = ?");
            $stmt->execute([$value, $user_id]);
            
            error_log("đŸŽĩ Profile field '$field' updated successfully for user: " . $user_id);
            echo json_encode(['success' => true, 'data' => ['field' => $field, 'value' => $value]]);
            break;
            
        case 'submit_rating':
            error_log("đŸŽĩ Rating submission requested for artist: " . ($_POST['artist_id'] ?? 'not specified'));
            error_log("đŸŽĩ POST data: " . print_r($_POST, true));
            
            if (!$user_id) {
                error_log("đŸŽĩ Rating submission failed: User not logged in");
                echo json_encode(['success' => false, 'message' => 'Please log in to submit a rating']);
                exit;
            }
            
            $artist_id = (int)($_POST['artist_id'] ?? 0);
            $rating = (int)($_POST['rating'] ?? 0);
            $comment = $_POST['comment'] ?? '';
            
            error_log("đŸŽĩ Parsed rating data: artist_id=$artist_id, rating=$rating, user_id=$user_id");
            
            if (!$artist_id) {
                error_log("đŸŽĩ Rating submission failed: Invalid artist_id");
                echo json_encode(['success' => false, 'message' => 'Invalid artist ID']);
                exit;
            }
            
            if ($rating < 1 || $rating > 10) {
                error_log("đŸŽĩ Rating submission failed: Invalid rating value ($rating)");
                echo json_encode(['success' => false, 'message' => 'Invalid rating. Please select a rating between 1 and 10.']);
                exit;
            }
            
            // Users can't rate themselves
            if ($artist_id === $user_id) {
                error_log("đŸŽĩ Rating submission failed: User trying to rate themselves");
                echo json_encode(['success' => false, 'message' => 'You cannot rate yourself']);
                exit;
            }
            
            try {
                // Check if table exists, if not create it
                $table_check = $pdo->query("SHOW TABLES LIKE 'artist_ratings'");
                if ($table_check->rowCount() == 0) {
                    // Create table without foreign keys first to avoid constraint issues
                    $pdo->exec("
                        CREATE TABLE artist_ratings (
                            id INT AUTO_INCREMENT PRIMARY KEY,
                            artist_id INT NOT NULL,
                            user_id INT NOT NULL,
                            rating INT NOT NULL,
                            comment TEXT,
                            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                            updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
                            UNIQUE KEY unique_rating (artist_id, user_id),
                            INDEX idx_artist_id (artist_id),
                            INDEX idx_user_id (user_id)
                        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
                    ");
                    error_log("đŸŽĩ Created artist_ratings table");
                }
                
                // Ensure rating constraint allows full 1-10 range
                ensureRatingConstraint($pdo, 'artist_ratings', 'chk_artist_rating');
                
                // Verify artist and user exist
                $check_artist = $pdo->prepare("SELECT id FROM users WHERE id = ?");
                $check_artist->execute([$artist_id]);
                if (!$check_artist->fetch()) {
                    throw new Exception("Artist with ID $artist_id does not exist");
                }
                
                $check_user = $pdo->prepare("SELECT id FROM users WHERE id = ?");
                $check_user->execute([$user_id]);
                if (!$check_user->fetch()) {
                    throw new Exception("User with ID $user_id does not exist");
                }
                
                // Insert or update rating with a retry if constraint needs adjustment
                $insertSql = "
                    INSERT INTO artist_ratings (artist_id, user_id, rating, comment) 
                    VALUES (?, ?, ?, ?) 
                    ON DUPLICATE KEY UPDATE 
                    rating = VALUES(rating), 
                    comment = VALUES(comment), 
                    updated_at = CURRENT_TIMESTAMP
                ";
                
                $insertParams = [$artist_id, $user_id, $rating, $comment];
                $insertSuccess = false;
                
                for ($attempt = 0; $attempt < 2; $attempt++) {
                    try {
                        $stmt = $pdo->prepare($insertSql);
                        $stmt->execute($insertParams);
                        $insertSuccess = true;
                        break;
                    } catch (PDOException $e) {
                        if ($attempt === 0 && isRatingConstraintError($e, 'artist_ratings')) {
                            error_log("đŸŽĩ Detected outdated artist rating constraint, attempting to fix...");
                            ensureRatingConstraint($pdo, 'artist_ratings', 'chk_artist_rating');
                            continue;
                        }
                        throw $e;
                    }
                }
                
                if (!$insertSuccess) {
                    throw new Exception("Failed to save rating after constraint adjustment");
                }
                
                // Get updated average rating and count
                $stmt = $pdo->prepare("SELECT AVG(rating) as avg_rating, COUNT(*) as total_ratings FROM artist_ratings WHERE artist_id = ?");
                $stmt->execute([$artist_id]);
                $rating_data = $stmt->fetch();
                
                // Verify the rating was actually saved
                $verify_stmt = $pdo->prepare("SELECT rating, comment FROM artist_ratings WHERE artist_id = ? AND user_id = ?");
                $verify_stmt->execute([$artist_id, $user_id]);
                $saved_rating = $verify_stmt->fetch();
                
                if (!$saved_rating) {
                    throw new Exception("Rating was not saved to database");
                }
                
                error_log("đŸŽĩ Rating submitted successfully: $rating/10 for artist $artist_id by user $user_id");
                error_log("đŸŽĩ Verified saved rating: " . print_r($saved_rating, true));
                
                echo json_encode([
                    'success' => true, 
                    'data' => [
                        'rating' => $rating,
                        'average_rating' => round($rating_data['avg_rating'], 1),
                        'total_ratings' => $rating_data['total_ratings']
                    ]
                ]);
            } catch (PDOException $e) {
                error_log("đŸŽĩ Rating submission PDO error: " . $e->getMessage());
                error_log("đŸŽĩ PDO error code: " . $e->getCode());
                error_log("đŸŽĩ Stack trace: " . $e->getTraceAsString());
                if ($e->getCode() === '23000' || (isset($e->errorInfo[2]) && strpos($e->errorInfo[2], 'artist_ratings.rating') !== false)) {
                    echo json_encode([
                        'success' => false,
                        'message' => 'Invalid rating value. Please choose a whole number between 1 and 10.'
                    ]);
                } else {
                    echo json_encode(['success' => false, 'message' => 'Database error: ' . $e->getMessage()]);
                }
            } catch (Exception $e) {
                error_log("đŸŽĩ Rating submission failed: " . $e->getMessage());
                error_log("đŸŽĩ Stack trace: " . $e->getTraceAsString());
                echo json_encode(['success' => false, 'message' => 'Failed to submit rating: ' . $e->getMessage()]);
            }
            break;
            
        case 'submit_track_rating':
            error_log("đŸŽĩ Track rating submission requested for track: " . ($_POST['track_id'] ?? 'not specified'));
            
            if (!$user_id) {
                echo json_encode(['success' => false, 'message' => 'Please log in to submit a track rating']);
                exit;
            }
            
            $track_id = (int)($_POST['track_id'] ?? 0);
            $rating = (int)($_POST['rating'] ?? 0);
            $comment = $_POST['comment'] ?? '';
            
            error_log("đŸŽĩ Track rating data: track_id=$track_id, rating=$rating, user_id=$user_id");
            
            if (!$track_id) {
                error_log("đŸŽĩ Track rating failed: Invalid track_id");
                echo json_encode(['success' => false, 'message' => 'Invalid track ID']);
                exit;
            }
            
            if ($rating < 1 || $rating > 10) {
                error_log("đŸŽĩ Track rating failed: Invalid rating value ($rating)");
                echo json_encode(['success' => false, 'message' => 'Invalid rating. Please select a rating between 1 and 10.']);
                exit;
            }
            
            try {
                // Check if track exists and user owns it
                $stmt = $pdo->prepare("SELECT user_id FROM music_tracks WHERE id = ?");
                $stmt->execute([$track_id]);
                $track = $stmt->fetch();
                
                if (!$track) {
                    error_log("đŸŽĩ Track rating failed: Track not found");
                    echo json_encode(['success' => false, 'message' => 'Track not found']);
                    exit;
                }
                
                // Ensure rating constraint allows full 1-10 range
                ensureRatingConstraint($pdo, 'track_ratings', 'chk_track_rating');
                
                // Insert or update rating with retry if needed
                $trackInsertSql = "
                    INSERT INTO track_ratings (track_id, user_id, rating, comment) 
                    VALUES (?, ?, ?, ?) 
                    ON DUPLICATE KEY UPDATE 
                        rating = VALUES(rating), 
                        comment = VALUES(comment), 
                        updated_at = CURRENT_TIMESTAMP
                ";
                
                $trackInsertParams = [$track_id, $user_id, $rating, $comment];
                $trackInsertSuccess = false;
                
                for ($attempt = 0; $attempt < 2; $attempt++) {
                    try {
                        $stmt = $pdo->prepare($trackInsertSql);
                        $stmt->execute($trackInsertParams);
                        $trackInsertSuccess = true;
                        break;
                    } catch (PDOException $insertException) {
                        if ($attempt === 0 && isRatingConstraintError($insertException, 'track_ratings')) {
                            error_log("đŸŽĩ Detected outdated track rating constraint, attempting to fix...");
                            ensureRatingConstraint($pdo, 'track_ratings', 'chk_track_rating');
                            continue;
                        }
                        throw $insertException;
                    }
                }
                
                if (!$trackInsertSuccess) {
                    throw new Exception("Failed to save track rating after constraint adjustment");
                }
                
                // Verify the rating was actually saved BEFORE calculating averages
                error_log("đŸŽĩ Verifying rating was saved: track_id=$track_id, user_id=$user_id");
                $verify_stmt = $pdo->prepare("SELECT id, rating, comment, created_at, updated_at FROM track_ratings WHERE track_id = ? AND user_id = ?");
                $verify_stmt->execute([$track_id, $user_id]);
                $saved_rating = $verify_stmt->fetch();
                
                if (!$saved_rating) {
                    error_log("đŸŽĩ ERROR: Verification query returned no rows!");
                    error_log("đŸŽĩ Attempting to check all ratings for this track...");
                    $all_ratings_stmt = $pdo->prepare("SELECT * FROM track_ratings WHERE track_id = ?");
                    $all_ratings_stmt->execute([$track_id]);
                    $all_ratings = $all_ratings_stmt->fetchAll();
                    error_log("đŸŽĩ All ratings for track $track_id: " . print_r($all_ratings, true));
                    throw new Exception("Rating was not saved to database - verification failed");
                }
                
                error_log("đŸŽĩ Verification successful! Saved rating: " . print_r($saved_rating, true));
                
                // Check if the saved rating matches what we tried to save
                if ($saved_rating['rating'] != $rating) {
                    error_log("đŸŽĩ WARNING: Saved rating ({$saved_rating['rating']}) does not match submitted rating ($rating)!");
                }
                
                // Get updated average rating and count
                $stmt = $pdo->prepare("SELECT AVG(rating) as avg_rating, COUNT(*) as total_ratings FROM track_ratings WHERE track_id = ?");
                $stmt->execute([$track_id]);
                $rating_data = $stmt->fetch();
                
                error_log("đŸŽĩ Track rating submitted successfully: $rating/10 for track $track_id by user $user_id");
                error_log("đŸŽĩ Average rating: {$rating_data['avg_rating']}, Total ratings: {$rating_data['total_ratings']}");
                
                echo json_encode([
                    'success' => true, 
                    'data' => [
                        'rating' => $rating,
                        'average_rating' => round($rating_data['avg_rating'], 1),
                        'total_ratings' => $rating_data['total_ratings']
                    ]
                ]);
            } catch (PDOException $e) {
                error_log("đŸŽĩ Track rating submission PDO error: " . $e->getMessage());
                error_log("đŸŽĩ PDO error code: " . $e->getCode());
                error_log("đŸŽĩ Stack trace: " . $e->getTraceAsString());
                if ($e->getCode() === '23000' || (isset($e->errorInfo[2]) && strpos($e->errorInfo[2], 'track_ratings.rating') !== false)) {
                    echo json_encode([
                        'success' => false,
                        'message' => 'Invalid rating value. Please choose a whole number between 1 and 10.'
                    ]);
                } else {
                    echo json_encode(['success' => false, 'message' => 'Database error: ' . $e->getMessage()]);
                }
            } catch (Exception $e) {
                error_log("đŸŽĩ Track rating submission failed: " . $e->getMessage());
                error_log("đŸŽĩ Stack trace: " . $e->getTraceAsString());
                echo json_encode(['success' => false, 'message' => 'Failed to submit track rating: ' . $e->getMessage()]);
            }
            break;
            
        case 'get_track_ratings':
            error_log("đŸŽĩ Getting track ratings for track: " . ($_GET['track_id'] ?? $_POST['track_id'] ?? 'not specified'));
            
            $track_id = (int)($_GET['track_id'] ?? $_POST['track_id'] ?? 0);
            
            if (!$track_id) {
                echo json_encode(['success' => false, 'message' => 'Track ID required']);
                exit;
            }
            
            try {
                // Get all ratings for this track with user information
                $stmt = $pdo->prepare("
                    SELECT 
                        tr.id,
                        tr.rating,
                        tr.comment,
                        tr.created_at,
                        tr.updated_at,
                        u.id as user_id,
                        u.name as user_name,
                        u.profile_image
                    FROM track_ratings tr
                    JOIN users u ON tr.user_id = u.id
                    WHERE tr.track_id = ?
                    ORDER BY tr.created_at DESC
                ");
                $stmt->execute([$track_id]);
                $ratings = $stmt->fetchAll(PDO::FETCH_ASSOC);
                
                // Get average rating and count
                $avg_stmt = $pdo->prepare("
                    SELECT 
                        AVG(rating) as average_rating,
                        COUNT(*) as total_ratings
                    FROM track_ratings
                    WHERE track_id = ?
                ");
                $avg_stmt->execute([$track_id]);
                $rating_stats = $avg_stmt->fetch();
                
                error_log("đŸŽĩ Found " . count($ratings) . " ratings for track $track_id");
                
                echo json_encode([
                    'success' => true,
                    'data' => [
                        'ratings' => $ratings,
                        'average_rating' => round($rating_stats['average_rating'] ?? 0, 1),
                        'total_ratings' => $rating_stats['total_ratings'] ?? 0
                    ]
                ]);
            } catch (Exception $e) {
                error_log("đŸŽĩ Error fetching track ratings: " . $e->getMessage());
                echo json_encode(['success' => false, 'message' => 'Failed to fetch ratings: ' . $e->getMessage()]);
            }
            break;
            
        case 'get_user_track_rating':
            error_log("đŸŽĩ Getting user's track rating for track: " . ($_GET['track_id'] ?? $_POST['track_id'] ?? 'not specified'));
            
            if (!$user_id) {
                echo json_encode(['success' => true, 'data' => ['rating' => null, 'comment' => null]]);
                exit;
            }
            
            $track_id = (int)($_GET['track_id'] ?? $_POST['track_id'] ?? 0);
            
            if (!$track_id) {
                echo json_encode(['success' => false, 'message' => 'Track ID required']);
                exit;
            }
            
            try {
                $stmt = $pdo->prepare("
                    SELECT rating, comment 
                    FROM track_ratings 
                    WHERE track_id = ? AND user_id = ?
                ");
                $stmt->execute([$track_id, $user_id]);
                $user_rating = $stmt->fetch(PDO::FETCH_ASSOC);
                
                if ($user_rating) {
                    error_log("đŸŽĩ Found user rating: " . print_r($user_rating, true));
                    echo json_encode([
                        'success' => true,
                        'data' => [
                            'rating' => (int)$user_rating['rating'],
                            'comment' => $user_rating['comment'] ?? ''
                        ]
                    ]);
                } else {
                    error_log("đŸŽĩ No existing rating found for user $user_id on track $track_id");
                    echo json_encode([
                        'success' => true,
                        'data' => [
                            'rating' => null,
                            'comment' => null
                        ]
                    ]);
                }
            } catch (Exception $e) {
                error_log("đŸŽĩ Failed to fetch user track rating: " . $e->getMessage());
                echo json_encode(['success' => false, 'message' => 'Failed to fetch user track rating: ' . $e->getMessage()]);
            }
            break;
        
        case 'get_user_artist_rating':
            error_log("đŸŽĩ Getting user's artist rating for artist: " . ($_GET['artist_id'] ?? $_POST['artist_id'] ?? 'not specified'));
            
            if (!$user_id) {
                echo json_encode(['success' => true, 'data' => ['rating' => null, 'comment' => null]]);
                exit;
            }
            
            $artist_id = (int)($_GET['artist_id'] ?? $_POST['artist_id'] ?? 0);
            
            if (!$artist_id) {
                echo json_encode(['success' => false, 'message' => 'Artist ID required']);
                exit;
            }
            
            try {
                $stmt = $pdo->prepare("
                    SELECT rating, comment 
                    FROM artist_ratings 
                    WHERE artist_id = ? AND user_id = ?
                ");
                $stmt->execute([$artist_id, $user_id]);
                $user_rating = $stmt->fetch(PDO::FETCH_ASSOC);
                
                if ($user_rating) {
                    error_log("đŸŽĩ Found user artist rating: " . print_r($user_rating, true));
                    echo json_encode([
                        'success' => true,
                        'data' => [
                            'rating' => (int)$user_rating['rating'],
                            'comment' => $user_rating['comment'] ?? ''
                        ]
                    ]);
                } else {
                    error_log("đŸŽĩ No existing artist rating found for user $user_id on artist $artist_id");
                    echo json_encode([
                        'success' => true,
                        'data' => [
                            'rating' => null,
                            'comment' => null
                        ]
                    ]);
                }
            } catch (Exception $e) {
                error_log("đŸŽĩ Failed to fetch user artist rating: " . $e->getMessage());
                echo json_encode(['success' => false, 'message' => 'Failed to fetch artist rating: ' . $e->getMessage()]);
            }
            break;
        
        case 'get_artist_rating_summary':
            $artist_id = (int)($_GET['artist_id'] ?? $_POST['artist_id'] ?? 0);
            if (!$artist_id) {
                echo json_encode(['success' => false, 'message' => 'Artist ID required']);
                exit;
            }
            
            try {
                $stmt = $pdo->prepare("
                    SELECT 
                        COALESCE(AVG(rating), 0) as average_rating,
                        COUNT(*) as total_ratings
                    FROM artist_ratings
                    WHERE artist_id = ?
                ");
                $stmt->execute([$artist_id]);
                $summary = $stmt->fetch(PDO::FETCH_ASSOC) ?: ['average_rating' => 0, 'total_ratings' => 0];
                
                echo json_encode([
                    'success' => true,
                    'data' => [
                        'average_rating' => round((float)$summary['average_rating'], 1),
                        'total_ratings' => (int)$summary['total_ratings']
                    ]
                ]);
            } catch (Exception $e) {
                error_log("đŸŽĩ Failed to fetch artist rating summary: " . $e->getMessage());
                echo json_encode(['success' => false, 'message' => 'Failed to fetch artist summary: ' . $e->getMessage()]);
            }
            break;
            
        default:
            echo json_encode(['success' => false, 'message' => 'Invalid action']);
            break;
    }
    
} catch (Exception $e) {
    error_log("Social API error: " . $e->getMessage());
    error_log("Social API error trace: " . $e->getTraceAsString());
    // Only show detailed error in development, generic message in production
    $error_message = (defined('DEBUG_MODE') && DEBUG_MODE) 
        ? 'An error occurred: ' . $e->getMessage()
        : 'An error occurred. Please try again.';
    echo json_encode(['success' => false, 'message' => $error_message]);
}
?> 

CasperSecurity Mini