<?php

declare(strict_types=1);

putenv('PATH=' . getenv('PATH') . ':/usr/bin:/usr/local/bin');

require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/db/config.php';

use Discord\Discord;
use Discord\Voice\VoiceClient;
use Discord\Parts\Channel\Channel;
use Discord\WebSockets\Event;
use Discord\WebSockets\Intents;
use Discord\Builders\CommandBuilder;
use Discord\Builders\MessageBuilder;
use Discord\Parts\Interactions\Interaction;
use Discord\Parts\Interactions\Command\Option;
use React\ChildProcess\Process;

$db = db();
// Ensure tables exist
$db->exec("CREATE TABLE IF NOT EXISTS bot_logs (id INT AUTO_INCREMENT PRIMARY KEY, message TEXT, log_level VARCHAR(50), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)");
$db->exec("CREATE TABLE IF NOT EXISTS bot_settings (setting_key VARCHAR(255) PRIMARY KEY, setting_value TEXT)");
$db->exec("CREATE TABLE IF NOT EXISTS bot_alarms (id INT AUTO_INCREMENT PRIMARY KEY, user_id VARCHAR(255) UNIQUE, guild_id VARCHAR(255), channel_id VARCHAR(255), alarm_time TIME, audio_url TEXT, is_active TINYINT(1) DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)");

// Default settings if not present
$stmt = $db->prepare("INSERT IGNORE INTO bot_settings (setting_key, setting_value) VALUES (?, ?)");
$stmt->execute(['sahur_time', '03:00']);
$stmt->execute(['voice_channel_id', '1457687430189682781']);
$stmt->execute(['sahur_source', 'sahur.mp3']);
$stmt->execute(['bot_token', '']); // User should set this manually or it might be already set

$settings = $db->query("SELECT setting_key, setting_value FROM bot_settings")->fetchAll(PDO::FETCH_KEY_PAIR);

$token = $settings['bot_token'] ?? '';
if (empty($token)) {
    // Try to get from env if missing in DB
    $token = getenv('BOT_TOKEN') ?: '';
}

if (empty($token)) {
    echo "Error: Bot token is missing. Please set it in bot_settings table or as BOT_TOKEN env var.\n";
    exit(1);
}

$discord = new Discord([
    'token' => $token,
    'intents' => Intents::getDefaultIntents() | Intents::GUILD_VOICE_STATES | Intents::MESSAGE_CONTENT,
]);

$voiceClient = null;
$joiningGuilds = [];
$ffmpegPath = trim((string)shell_exec("command -v ffmpeg"));

/**
 * Safely join a voice channel by ensuring any existing connection is closed first.
 */
function safeJoin(Discord $discord, $channel, $interaction = null, $onSuccess = null) {
    global $joiningGuilds;
    $guildId = $channel->guild_id;

    // Check if we are already in the correct channel
    $vc = $discord->getVoiceClient($guildId);
    if ($vc) {
        if ((string)$vc->channel->id === (string)$channel->id) {
            $isReady = method_exists($vc, 'isReady') ? $vc->isReady() : ($vc->ready ?? false);
            if ($isReady) {
                logToDb("Already in channel {$channel->name} and ready.");
                unset($joiningGuilds[$guildId]);
                if ($onSuccess) $onSuccess($vc);
                return;
            } else {
                logToDb("Already in channel {$channel->name} but not ready yet. Waiting...");
                // Don't start a new join, just wait or if stuck, clear and try again
                if (!isset($joiningGuilds[$guildId])) {
                    $joiningGuilds[$guildId] = true;
                }
                $vc->once('ready', function() use ($onSuccess, $vc, $guildId) {
                    global $joiningGuilds;
                    unset($joiningGuilds[$guildId]);
                    if ($onSuccess) $onSuccess($vc);
                });
                return;
            }
        } else {
            logToDb("In different channel ({$vc->channel->name}), closing old connection first.");
            $vc->close();
            // Fall through to the timer logic below if needed, or just let it proceed
        }
    }

    if (isset($joiningGuilds[$guildId]) && $joiningGuilds[$guildId] === true) {
        logToDb("Join already in progress for guild $guildId");
        if ($interaction) {
            try {
                $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("⏳ A join request is already in progress..."));
            } catch (\Throwable $e) {}
        }
        return;
    }

    $joiningGuilds[$guildId] = true;
    
    // Safety timeout: clear joining state after 45 seconds
    $discord->getLoop()->addTimer(45.0, function() use ($guildId) {
        global $joiningGuilds;
        if (isset($joiningGuilds[$guildId]) && $joiningGuilds[$guildId] === true) {
            logToDb("Safety timeout: Clearing stuck join state for guild $guildId");
            unset($joiningGuilds[$guildId]);
        }
    });

    $doJoin = function() use ($discord, $channel, $interaction, $onSuccess, $guildId) {
        global $joiningGuilds;
        
        logToDb("Executing joinVoiceChannel for guild $guildId, channel " . $channel->name);
        $discord->joinVoiceChannel($channel, false, false)->then(function (VoiceClient $vc) use ($onSuccess, $guildId, $discord) {
            global $joiningGuilds;
            $joiningGuilds[$guildId] = 'connected'; // Mark as connected but maybe not ready
            logToDb("Successfully joined " . $vc->channel->name);
            
            $vc->on('error', function ($e) use ($guildId) {
                logToDb("VoiceClient Error in guild $guildId: " . $e->getMessage(), 'error');
            });

            $vc->on('close', function () use ($guildId) {
                global $joiningGuilds;
                unset($joiningGuilds[$guildId]);
                logToDb("VoiceClient Closed in guild $guildId");
            });

            $isReady = method_exists($vc, 'isReady') ? $vc->isReady() : ($vc->ready ?? false);
            if ($isReady) {
                unset($joiningGuilds[$guildId]);
                if ($onSuccess) $onSuccess($vc);
            } else {
                $vc->once('ready', function() use ($onSuccess, $vc, $guildId) {
                    global $joiningGuilds;
                    unset($joiningGuilds[$guildId]);
                    if ($onSuccess) $onSuccess($vc);
                });
            }
        }, function ($e) use ($interaction, $guildId, $discord) {
            global $joiningGuilds;
            unset($joiningGuilds[$guildId]);
            logToDb("Error joining VC in guild $guildId: " . $e->getMessage(), 'error');
            
            if ($interaction) {
                try {
                    $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("❌ Error joining voice channel: " . $e->getMessage()));
                } catch (\Throwable $err) {}
            }
        });
    };

    // If we just closed a VC, wait a bit for Discord to catch up
    if ($vc) {
        $discord->getLoop()->addTimer(1.5, $doJoin);
    } else {
        $doJoin();
    }
}

function logToDb($message, $level = 'info') {
    // Basic safety check for DB connection
    static $db_working = true;
    if (!$db_working && $level !== 'error') return;

    // Echo to console anyway
    echo "[" . date('Y-m-d H:i:s') . "] [$level] $message\n";

    try {
        $db = db();
        $stmt = $db->prepare("INSERT INTO bot_logs (message, log_level) VALUES (?, ?)");
        $stmt->execute([$message, $level]);
    } catch (\Throwable $e) {
        $db_working = false;
        // Don't echo again to avoid loops if echo itself fails (unlikely)
    }
}

$discord->on('ready', function (Discord $discord) {
    echo "Bot is ready as " . $discord->user->username . "#" . $discord->user->discriminator . PHP_EOL;
    logToDb("Bot is online and ready: " . $discord->user->username);

    $db = db();
    $db->prepare("INSERT INTO bot_settings (setting_key, setting_value) VALUES ('bot_status', 'online') ON DUPLICATE KEY UPDATE setting_value = 'online'")->execute();

    // Listen for voice state updates to debug disconnecting issues
    $discord->on(Event::VOICE_STATE_UPDATE, function ($state, Discord $discord) {
        if ($state->user_id == $discord->id) {
            $channelName = 'None';
            if ($state->channel_id) {
                $channel = $discord->getChannel($state->channel_id);
                $channelName = $channel ? $channel->name : $state->channel_id;
            }
            logToDb("Bot voice state updated: Channel=" . $channelName);
            
            // If the bot was disconnected or moved
            if ($state->channel_id === null) {
                logToDb("Bot was disconnected from voice channel. Cleaning up...");
                global $joiningGuilds;
                unset($joiningGuilds[$state->guild_id]);
                
                // Ensure VoiceClient is closed if it still exists
                $vc = $discord->getVoiceClient($state->guild_id);
                if ($vc) {
                    try { $vc->close(); } catch (\Throwable $e) {}
                }
            }
        }
    });

    // Periodic timer for Sahur and Alarms
    $discord->getLoop()->addPeriodicTimer(60, function () use ($discord) {
        $now = date('H:i');
        $db = db();
        
        $settings = $db->query("SELECT setting_key, setting_value FROM bot_settings")->fetchAll(PDO::FETCH_KEY_PAIR);
        $sahurTime = $settings['sahur_time'] ?? '03:00';
        $vcId = $settings['voice_channel_id'] ?? '1457687430189682781';

        if ($now === $sahurTime) {
            echo "Sahur time triggered ($now)\n";
            logToDb("Sahur time triggered.");
            playSahur($discord, $vcId);
        }

        $stmt = $db->prepare("SELECT * FROM bot_alarms WHERE alarm_time LIKE ? AND is_active = 1");
        $stmt->execute([$now . '%']);
        $alarms = $stmt->fetchAll(PDO::FETCH_ASSOC);
        
        $alarmCount = 0;
        foreach ($alarms as $alarm) {
            $alarmCount++;
            $discord->getLoop()->addTimer($alarmCount * 2.0, function() use ($discord, $alarm) {
                echo "Alarm triggered for user {$alarm['user_id']} at {$alarm['alarm_time']}\n";
                logToDb("Alarm triggered for user {$alarm['user_id']}");
                playAlarm($discord, $alarm);
            });
        }
    });

    // Register Commands (Uncomment if you need to update commands)
    registerCommands($discord);
    echo "Commands registration updated.\n";
});

function getOptionValue($options, $name) {
    if (!$options) return null;
    foreach ($options as $option) {
        if ($option->name === $name) {
            return $option->value;
        }
    }
    return null;
}

function registerCommands(Discord $discord) {
    $commands = [
        CommandBuilder::new()->setName('join')->setDescription('Join your current voice channel'),
        CommandBuilder::new()->setName('rejoin')->setDescription('Force rejoin the voice channel'),
        CommandBuilder::new()->setName('out')->setDescription('Leave the voice channel'),
        CommandBuilder::new()->setName('status')->setDescription('Check bot status'),
        CommandBuilder::new()->setName('play')->setDescription('Play music from URL')
            ->addOption((new Option($discord))->setName('url')->setDescription('YouTube/TikTok/SoundCloud URL')->setType(Option::STRING)->setRequired(true)),
        CommandBuilder::new()->setName('stop')->setDescription('Stop music'),
        CommandBuilder::new()->setName('settime')->setDescription('Set your personal alarm time (HH:MM)')
            ->addOption((new Option($discord))->setName('time')->setDescription('Time in HH:MM format')->setType(Option::STRING)->setRequired(true)),
        CommandBuilder::new()->setName('setalarm')->setDescription('Set your personal alarm audio link')
            ->addOption((new Option($discord))->setName('link')->setDescription('Audio URL for the alarm')->setType(Option::STRING)->setRequired(true)),
        CommandBuilder::new()->setName('help')->setDescription('Show help information'),
        CommandBuilder::new()->setName('ping')->setDescription('Check if bot is responsive'),
        CommandBuilder::new()->setName('where')->setDescription('Show which voice channel the bot is in'),
    ];

    foreach ($commands as $command) {
        $discord->application->commands->save($discord->application->commands->create($command->toArray()));
    }
}

function streamAudio(VoiceClient $vc, string $url, $interaction = null) {
    global $discord;
    echo "Streaming audio: $url\n";
    if ($interaction) {
        $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("🎶 Loading audio info from link..."));
    }

    $userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36";
    $safeUrl = escapeshellarg($url);
    
    // Handle Spotify differently by searching on YouTube
    if (strpos($url, 'spotify.com') !== false) {
        logToDb("Spotify link detected, searching on YouTube...");
        if ($interaction) {
            $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("🎶 Spotify link detected, searching for audio on YouTube..."));
        }
        $cmd = "yt-dlp --no-warnings --no-check-certificates --print \"%(title)s\" --print \"%(url)s\" -f \"bestaudio[ext=m4a]/bestaudio/best\" --no-playlist --user-agent \"$userAgent\" \"ytsearch1:" . $url . "\"";
    } else {
        $cmd = "yt-dlp --no-warnings --no-check-certificates --print \"%(title)s\" --print \"%(url)s\" -f \"bestaudio[ext=m4a]/bestaudio/best\" --no-playlist --user-agent \"$userAgent\" $safeUrl";
    }

    $process = new Process($cmd);
    
    try {
        $process->start($discord->getLoop());
    } catch (\Exception $e) {
        echo "Failed to start yt-dlp process: " . $e->getMessage() . "\n";
        if ($interaction) {
            $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("❌ Failed to start audio downloader."));
        }
        return;
    }

    $timer = $discord->getLoop()->addTimer(60.0, function() use ($process, $interaction) {
        if ($process->isRunning()) {
            echo "yt-dlp process timed out after 60s\n";
            $process->terminate(SIGKILL);
            if ($interaction) {
                $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("❌ Audio loading timed out (60s)."));
            }
        }
    });

    $output = '';
    $errorOutput = '';

    $process->stdout->on('data', function ($chunk) use (&$output) {
        $output .= $chunk;
    });

    $process->stderr->on('data', function ($chunk) use (&$errorOutput) {
        $errorOutput .= $chunk;
    });

    $process->on('exit', function ($code) use (&$output, &$errorOutput, $vc, $interaction, $url, $discord, $timer) {
        $discord->getLoop()->cancelTimer($timer);
        
        $outputLines = explode("\n", trim($output));
        $lines = array_values(array_filter($outputLines, function($line) {
            $trimmed = trim($line);
            return !empty($trimmed) && strpos($trimmed, 'WARNING:') !== 0 && strpos($trimmed, 'ERROR:') !== 0;
        }));

        if ($code === 0 && count($lines) >= 2) {
            $title = trim($lines[0]);
            // Find the last line that looks like a URL
            $streamUrl = '';
            for ($i = count($lines) - 1; $i >= 0; $i--) {
                if (filter_var(trim($lines[$i]), FILTER_VALIDATE_URL)) {
                    $streamUrl = trim($lines[$i]);
                    break;
                }
            }

            if (empty($streamUrl)) {
                echo "Failed to find valid stream URL in yt-dlp output for $url\n";
                if ($interaction) {
                    $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("❌ Failed to parse audio stream URL."));
                }
                return;
            }

            echo "Title: $title, Stream URL found: " . substr($streamUrl, 0, 60) . "...\n";
            if ($interaction) {
                $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("🎶 **Now Playing:** $title"));
            }

            $playAttempted = false;
            $isClosed = false;
            $retryCount = 0;
            $maxRetries = 25;
            $playFunc = function ($isFallback = false) use (&$playFunc, &$playAttempted, &$isClosed, &$retryCount, $maxRetries, $vc, $streamUrl, $title, $interaction) {
                if ($isClosed) return;
                
                $isReady = method_exists($vc, 'isReady') ? $vc->isReady() : ($vc->ready ?? false);
                echo "Attempting to play $title. VC Ready: " . ($isReady ? 'Yes' : 'No') . " (Retry: $retryCount/$maxRetries, Fallback: " . ($isFallback ? 'Yes' : 'No') . ")\n";

                if (!$isReady) {
                    if ($retryCount < $maxRetries) {
                        $retryCount++;
                        $vc->discord->getLoop()->addTimer(2.0, function() use (&$playFunc) {
                            $playFunc();
                        });
                    } else {
                        echo "Failed to play $title after $maxRetries retries: Voice client never became ready.\n";
                        if ($interaction) {
                            $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("❌ Failed to play: Voice client timed out waiting to be ready."));
                        }
                    }
                    return;
                }

                if ($playAttempted && !$isFallback) return;
                $playAttempted = true;

                echo "Calling playFile for $title...\n";
                $vc->playFile($streamUrl)->then(function() use ($title) {
                    echo "Finished playing $title\n";
                }, function($e) use ($vc, $streamUrl, $title, &$playFunc, &$playAttempted, &$retryCount, $maxRetries, $interaction) {
                    echo "Error playing $title: " . $e->getMessage() . "\n";
                    logToDb("Error playing $title: " . $e->getMessage(), 'error');
                    
                    if ($retryCount < $maxRetries && (strpos($e->getMessage(), 'ready') !== false || strpos($e->getMessage(), 'connected') !== false || strpos($e->getMessage(), 'Voice Client') !== false)) {
                        $retryCount++;
                        $playAttempted = false;
                        echo "Retrying $title (attempt $retryCount/$maxRetries) in 3 seconds...\n";
                        $vc->discord->getLoop()->addTimer(3.0, function() use (&$playFunc) {
                            $playFunc();
                        });
                    } else {
                        if ($interaction) {
                            $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("❌ Error playing audio: " . $e->getMessage()));
                        }
                    }
                });
            };

            $isReady = method_exists($vc, 'isReady') ? $vc->isReady() : ($vc->ready ?? false);
            if ($isReady) {
                $playFunc();
            } else {
                echo "Voice client not ready yet for $title (initial check), adding listeners...\n";
                
                $fallbackTimer = $vc->discord->getLoop()->addTimer(7.0, function() use ($vc, $playFunc, $title, &$isClosed) {
                    if ($isClosed) return;
                    $isReady = method_exists($vc, 'isReady') ? $vc->isReady() : ($vc->ready ?? false);
                    if (!$isReady) {
                        echo "Still not ready after 7s for $title, trying playFunc as fallback.\n";
                        $playFunc(true);
                    }
                });

                $safetyTimer = $vc->discord->getLoop()->addTimer(60.0, function () use ($vc, $interaction, $title, $fallbackTimer, &$isClosed) {
                    if ($isClosed) return;
                    try {
                        $vc->discord->getLoop()->cancelTimer($fallbackTimer);
                        
                        $isReady = method_exists($vc, 'isReady') ? $vc->isReady() : ($vc->ready ?? false);
                        if (!$isReady) {
                            echo "Timed out waiting for voice client to be ready for $title after 60s\n";
                            if ($interaction) {
                                $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("⚠️ Voice client timed out. Use `/out` and try again."));
                            }
                        }
                    } catch (\Exception $e) {}
                });

                $vc->once('ready', function() use ($vc, &$playFunc, $fallbackTimer, $safetyTimer, &$isClosed) { 
                    if ($isClosed) return;
                    $vc->discord->getLoop()->cancelTimer($fallbackTimer);
                    $vc->discord->getLoop()->cancelTimer($safetyTimer);
                    $playFunc(); 
                });

                $vc->once('close', function() use ($vc, $interaction, $title, $fallbackTimer, $safetyTimer, &$isClosed) {
                    $isClosed = true;
                    $vc->discord->getLoop()->cancelTimer($fallbackTimer);
                    $vc->discord->getLoop()->cancelTimer($safetyTimer);
                    if ($interaction) {
                        $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("⚠️ Voice connection closed."));
                    }
                });
            }
        } else {
            echo "Failed to fetch stream URL for $url. Code: $code\nError Output: $errorOutput\n";
            $errorMessage = "❌ Failed to fetch audio. Link may be invalid or blocked.";
            if (strpos($errorOutput, 'blocked') !== false || strpos($errorOutput, '403') !== false) {
                $errorMessage = "❌ This platform is blocking the bot's IP. Try a YouTube link instead.";
            }
            if ($interaction) {
                $interaction->updateOriginalResponse(MessageBuilder::new()->setContent($errorMessage));
            }
        }
    });
}

function playSahur(Discord $discord, $vcId) {
    $channel = $discord->getChannel($vcId);
    if (!$channel) return;

    safeJoin($discord, $channel, null, function($vc) {
        $db = db();
        $source = $db->query("SELECT setting_value FROM bot_settings WHERE setting_key = 'sahur_source'")->fetchColumn() ?: 'sahur.mp3';
        
        $playAction = function() use ($vc, $source) {
            if (filter_var($source, FILTER_VALIDATE_URL)) {
                streamAudio($vc, $source);
            } else if (file_exists($source)) {
                $vc->playFile($source);
            }
        };

        $isReady = method_exists($vc, 'isReady') ? $vc->isReady() : ($vc->ready ?? false);
        if ($isReady) {
            $playAction();
        } else {
            $vc->once('ready', $playAction);
            $vc->discord->getLoop()->addTimer(10.0, function() use ($vc, $playAction) {
                $isReady = method_exists($vc, 'isReady') ? $vc->isReady() : ($vc->ready ?? false);
                if (!$isReady) $playAction();
            });
        }
    });
}

function playAlarm(Discord $discord, $alarm) {
    $channel = $discord->getChannel($alarm['channel_id']);
    if (!$channel) return;

    safeJoin($discord, $channel, null, function($vc) use ($alarm) {
        $isReady = method_exists($vc, 'isReady') ? $vc->isReady() : ($vc->ready ?? false);
        if ($isReady) {
            streamAudio($vc, $alarm['audio_url']);
        } else {
            $vc->once('ready', function() use ($vc, $alarm) {
                streamAudio($vc, $alarm['audio_url']);
            });
            $vc->discord->getLoop()->addTimer(10.0, function() use ($vc, $alarm) {
                $isReady = method_exists($vc, 'isReady') ? $vc->isReady() : ($vc->ready ?? false);
                if (!$isReady) streamAudio($vc, $alarm['audio_url']);
            });
        }
    });
}

$discord->on(Event::INTERACTION_CREATE, function (Interaction $interaction, Discord $discord) use ($ffmpegPath) {
    if ($interaction->type !== 2) return;
    
    // 1. Acknowledge IMMEDIATELY to win the 3-second race with Discord
    $interaction->acknowledge();

    $command = $interaction->data->name;
    logToDb("Received interaction: $command from " . ($interaction->member->user->username ?? "Unknown"));

    try {
        switch ($command) {
            case 'ping':
                $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("Pong! 🏓"));
                break;

            case 'help':
                $interaction->updateOriginalResponse(MessageBuilder::new()->setContent(
                    "**AsepSahur Bot Commands:**\n" .
                    "`/join` - Join your voice channel\n" .
                    "`/play [url]` - Play music from URL\n" .
                    "`/stop` - Stop current playback\n" .
                    "`/settime [HH:MM]` - Set your alarm time\n" .
                    "`/setalarm [url]` - Set your alarm audio\n" .
                    "`/status` - Check bot status\n" .
                    "`/ping` - Test bot responsiveness\n" .
                    "`/out` - Make bot leave voice channel\n" .
                    "`/reset` - Hard reset voice state"
                ));
                break;

            case 'status':
                $vc = $discord->getVoiceClient($interaction->guild_id);
                $isReady = $vc ? (method_exists($vc, 'isReady') ? $vc->isReady() : ($vc->ready ?? false)) : false;
                
                $db = db();
                $sahurTime = $db->query("SELECT setting_value FROM bot_settings WHERE setting_key = 'sahur_time'")->fetchColumn() ?: '03:00';
                
                $status = "🤖 **Bot Status:** Online\n";
                $status .= "🔊 **Voice:** " . ($vc ? "Connected to **" . $vc->channel->name . "**" . ($isReady ? " ✅" : " ⏳") : "Disconnected ❌") . "\n";
                $status .= "⏰ **Sahur Time:** " . $sahurTime . "\n";
                $status .= "🛠 **FFmpeg:** " . ($ffmpegPath ? "✅" : "❌") . "\n";
                
                $interaction->updateOriginalResponse(MessageBuilder::new()->setContent($status));
                break;

            case 'where':
                $guild = $discord->guilds->get('id', $interaction->guild_id);
                $voiceState = $guild ? $guild->voice_states->get('user_id', $discord->id) : null;
                $vc = $discord->getVoiceClient($interaction->guild_id);
                
                $msg = "";
                if ($voiceState && $voiceState->channel_id) {
                    $channel = $discord->getChannel($voiceState->channel_id);
                    $msg .= "📍 **Discord thinks I am in:** " . ($channel ? $channel->name : $voiceState->channel_id) . "\n";
                } else {
                    $msg .= "📍 **Discord thinks I am:** Not in a channel\n";
                }
                
                if ($vc) {
                    $isReady = method_exists($vc, 'isReady') ? $vc->isReady() : ($vc->ready ?? false);
                    $msg .= "🔊 **VoiceClient state:** Connected to **" . $vc->channel->name . "** (" . ($isReady ? "Ready ✅" : "Connecting ⏳") . ")";
                } else {
                    $msg .= "🔊 **VoiceClient state:** No active connection ❌";
                }
                
                $interaction->updateOriginalResponse(MessageBuilder::new()->setContent($msg));
                break;

            case 'reset':
                global $joiningGuilds;
                $joiningGuilds = [];
                $vc = $discord->getVoiceClient($interaction->guild_id);
                if ($vc) {
                    try { $vc->close(); } catch (\Throwable $e) {}
                }
                $discord->getLoop()->addTimer(0.5, function() use ($discord, $interaction) {
                    $discord->selectVoiceChannel(null);
                    $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("🔄 **Hard Reset complete.** Voice states cleared. Try `/join` again."));
                });
                break;

            case 'rejoin':
            case 'join':
                $userChannel = $interaction->member->getVoiceChannel();
                if (!$userChannel) {
                    $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("❌ You must be in a voice channel!"));
                    return;
                }
                
                // Clear state for this guild if it's a rejoin
                if ($command === 'rejoin') {
                    global $joiningGuilds;
                    unset($joiningGuilds[$interaction->guild_id]);
                }

                $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("⏳ Joining " . $userChannel->name . "..."));
                
                safeJoin($discord, $userChannel, $interaction, function($vc) use ($interaction) {
                    try {
                        $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("✅ Joined " . $vc->channel->name));
                    } catch (\Throwable $e) {}
                });
                break;

            case 'play':
                $opts = $interaction->data->options;
                $url = getOptionValue($opts, 'url');
                if (!$url) {
                    $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("❌ URL is missing!"));
                    return;
                }
                $userChannel = $interaction->member->getVoiceChannel();
                if (!$userChannel) {
                    $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("❌ Join a VC first!"));
                    return;
                }

                $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("⏳ Joining and preparing audio..."));
                
                safeJoin($discord, $userChannel, $interaction, function($vc) use ($url, $interaction) {
                    $vc->discord->getLoop()->addTimer(1.0, function() use ($vc, $url, $interaction) {
                        streamAudio($vc, $url, $interaction);
                    });
                });
                break;

            case 'stop':
                $vc = $discord->getVoiceClient($interaction->guild_id);
                if ($vc) {
                    $vc->stop();
                    $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("Stopped playback."));
                } else {
                    $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("Nothing is playing."));
                }
                break;

            case 'out':
                global $joiningGuilds;
                unset($joiningGuilds[$interaction->guild_id]);
                
                $vc = $discord->getVoiceClient($interaction->guild_id);
                if ($vc) {
                    try {
                        $vc->close();
                        $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("✅ Left voice channel."));
                    } catch (\Throwable $e) {
                        $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("✅ Left with error: " . $e->getMessage()));
                    }
                } else {
                    $discord->selectVoiceChannel(null);
                    $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("💨 Requested to leave voice channel."));
                }
                break;

            case 'settime':
                $opts = $interaction->data->options;
                $time = getOptionValue($opts, 'time');
                if (!$time || !preg_match('/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/', $time)) {
                    $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("❌ Invalid format. Use HH:MM (e.g. 03:00)"));
                    return;
                }
                $userId = (string)$interaction->member->id;
                $guildId = (string)$interaction->guild_id;
                $channel = $interaction->member->getVoiceChannel();
                $channelId = (string)($channel->id ?? '');
                
                if (empty($channelId)) {
                    $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("❌ Join a voice channel first!"));
                    return;
                }

                $db = db();
                $stmt = $db->prepare("INSERT INTO bot_alarms (user_id, guild_id, channel_id, alarm_time, audio_url) 
                                     VALUES (?, ?, ?, ?, 'sahur.mp3') 
                                     ON DUPLICATE KEY UPDATE alarm_time = ?, guild_id = ?, channel_id = ?");
                $stmt->execute([$userId, $guildId, $channelId, $time, $time, $guildId, $channelId]);
                $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("✅ Alarm set to $time in " . $channel->name));
                break;

            case 'setalarm':
                $opts = $interaction->data->options;
                $link = getOptionValue($opts, 'link');
                if (!$link) {
                    $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("❌ Link is missing!"));
                    return;
                }
                $userId = (string)$interaction->member->id;
                $guildId = (string)$interaction->guild_id;
                $channel = $interaction->member->getVoiceChannel();
                $channelId = (string)($channel->id ?? '');

                if (empty($channelId)) {
                    $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("❌ Join a voice channel first!"));
                    return;
                }

                $db = db();
                $stmt = $db->prepare("INSERT INTO bot_alarms (user_id, guild_id, channel_id, alarm_time, audio_url) 
                                     VALUES (?, ?, ?, '03:00', ?) 
                                     ON DUPLICATE KEY UPDATE audio_url = ?, guild_id = ?, channel_id = ?");
                $stmt->execute([$userId, $guildId, $channelId, $link, $link, $guildId, $channelId]);
                $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("✅ Alarm audio updated."));
                break;
        }
    } catch (\Throwable $e) {
        logToDb("Command Error ($command): " . $e->getMessage(), 'error');
        try {
            $interaction->updateOriginalResponse(MessageBuilder::new()->setContent("❌ An unexpected error occurred: " . $e->getMessage()));
        } catch (\Throwable $e2) {}
    }
});

$discord->run();
