<?php

declare(strict_types=1);

namespace App\Http\Controllers\Market;

use App\Enums\UserActions;
use App\Http\Controllers\Helpers\Permissions\UserPermissions;
use App\Http\Controllers\Helpers\ResponseHelper;
use App\Models\User;
use App\Models\Market\Store;
use App\Models\Market\Product;
use App\Models\GlobalTransaction;
use App\Models\FileContent;
use App\Models\DbBackup;
use Hyperf\Stringable\Str;

use Hypervel\Http\Request;
use Hypervel\Support\Facades\Auth;
use Hypervel\Support\Facades\DB;
use Hypervel\Support\Facades\Redis;
use Hypervel\Support\Facades\Response;

class UltimateController
{
    /**
     * Common check for Ultimate access.
     */
    private function checkAccess()
    {
        if (!UserPermissions::isActionPermitted(0, UserActions::UltimateConsole)) {
            return false;
        }
        return true;
    }

    /**
     * Get system-wide statistics for the dashboard.
     */
    public function getSystemStats()
    {
        if (!$this->checkAccess()) return ResponseHelper::returnUnauthorized();

        $globalMessage = Redis::get('system:global_message');

        $redisStatus = ['connected' => false, 'ping_ms' => null, 'used_memory_human' => null, 'version' => null, 'error' => null];
        try {
            $start = microtime(true);
            $pong = Redis::ping();
            $redisStatus['ping_ms'] = round((microtime(true) - $start) * 1000, 2);
            $redisStatus['connected'] = $pong === true || $pong === 'PONG' || $pong === '+PONG' || (is_string($pong) && stripos($pong, 'PONG') !== false);

            $info = Redis::info();
            if (is_array($info)) {
                $flat = isset($info['Memory']) && is_array($info['Memory']) ? $info['Memory'] : $info;
                $redisStatus['used_memory_human'] = $flat['used_memory_human'] ?? null;
                $serverInfo = isset($info['Server']) && is_array($info['Server']) ? $info['Server'] : $info;
                $redisStatus['version'] = $serverInfo['redis_version'] ?? null;
            }
        } catch (\Throwable $e) {
            $redisStatus['error'] = $e->getMessage();
        }

        $stats = [
            'users' => User::count(),
            'active_users' => User::where('active', true)->count(),
            'stores' => Store::count(),
            'active_stores' => Store::where('is_active', true)->count(),
            'products' => Product::count(),
            'transactions' => GlobalTransaction::count(),
            'total_balance' => GlobalTransaction::sum('amount'),
            'php_version' => PHP_VERSION,
            'server_time' => date('Y-m-d H:i:s'),
            'maintenance_mode' => Redis::get('system:maintenance_mode') === 'true',
            'global_message' => $globalMessage ? json_decode($globalMessage, true) : null,
            'logs_count' => DB::table('logs')->count(),
            'table_logs_count' => DB::table('table_logs')->count(),
            'pos_sessions_count' => DB::table('pos_sessions')->count(),
            'cooperatives_count' => DB::table('organizations')->where('type', 'COOPERATIVE')->count(),
            'carts_count' => DB::table('carts')->count(),
            'farmer_profiles_count' => DB::table('farmer_profiles')->count(),
            'redis' => $redisStatus,
        ];

        return Response::json(['success' => true, 'data' => $stats]);
    }

    /**
     * Execute a raw SQL query.
     */
    public function runQuery(Request $request)
    {
        if (Auth::user()->acct_type !== \App\Enums\UserTypes::ULTIMATE || !UserPermissions::isActionPermitted(0, UserActions::UltimateQuery)) {
            return ResponseHelper::returnUnauthorized();
        }

        $query = $request->input('query');
        if (empty($query)) return ResponseHelper::returnError('Query cannot be empty');

        try {
            $queryLower = strtolower(trim($query));
            if (str_starts_with($queryLower, 'select') || str_starts_with($queryLower, 'show') || str_starts_with($queryLower, 'describe')) {
                $results = DB::select($query);
                return Response::json(['success' => true, 'data' => $results]);
            } else {
                $affected = DB::statement($query);
                return Response::json(['success' => true, 'affected' => $affected]);
            }
        } catch (\Throwable $th) {
            return ResponseHelper::returnError($th->getMessage());
        }
    }

    /**
     * Toggle maintenance mode system-wide.
     */
    public function toggleMaintenance(Request $request)
    {
        if (!$this->checkAccess() || !UserPermissions::isActionPermitted(0, UserActions::UltimateMaintenance)) {
            return ResponseHelper::returnUnauthorized();
        }

        $enabled = (bool) $request->input('enabled');
        Redis::set('system:maintenance_mode', $enabled ? 'true' : 'false');

        return Response::json(['success' => true, 'maintenance_mode' => $enabled]);
    }

    /**
     * Send a global message / broadcast.
     */
    public function sendGlobalMessage(Request $request)
    {
        if (!$this->checkAccess() || !UserPermissions::isActionPermitted(0, UserActions::UltimateGlobalMessage)) {
            return ResponseHelper::returnUnauthorized();
        }

        $message = $request->input('message');
        $type = $request->input('type', 'info'); // info, success, warning, danger

        if (empty($message)) {
            Redis::del('system:global_message');
            return Response::json(['success' => true, 'message' => 'Global message cleared']);
        }

        Redis::set('system:global_message', json_encode([
            'text' => $message,
            'type' => $type,
            'timestamp' => time()
        ]));

        return Response::json(['success' => true]);
    }

    /**
     * Flush / Truncate specific tables.
     */
    public function flushData(Request $request)
    {
        if (!$this->checkAccess() || !UserPermissions::isActionPermitted(0, UserActions::UltimateFlush)) {
            return ResponseHelper::returnUnauthorized();
        }

        $target = $request->input('target');
        
        try {
            $affected = 0;
            switch ($target) {
                case 'transactions':
                    $affected = GlobalTransaction::count();
                    DB::statement('SET FOREIGN_KEY_CHECKS = 0');
                    GlobalTransaction::truncate();
                    DB::statement('SET FOREIGN_KEY_CHECKS = 1');
                    break;
                case 'pos_sessions':
                    $affected = DB::table('pos_sessions')->count();
                    DB::table('pos_sessions')->truncate();
                    break;
                case 'cache':
                    Redis::flushDB();
                    break;
                case 'stores':
                    $affected = DB::table('str')->count();
                    DB::statement('SET FOREIGN_KEY_CHECKS = 0');
                    DB::table('prd_str')->truncate();
                    DB::table('store_managers')->truncate();
                    DB::table('str')->truncate();
                    DB::statement('SET FOREIGN_KEY_CHECKS = 1');
                    break;
                case 'products':
                    $affected = DB::table('prd_items')->count();
                    DB::statement('SET FOREIGN_KEY_CHECKS = 0');
                    DB::table('prd_str')->truncate();
                    DB::table('prd_items')->truncate();
                    DB::statement('SET FOREIGN_KEY_CHECKS = 1');
                    break;
                case 'cooperatives':
                    $affected = DB::table('organizations')->where('type', 'COOPERATIVE')->count();
                    DB::statement('SET FOREIGN_KEY_CHECKS = 0');
                    DB::table('cooperative_votes')->truncate();
                    DB::table('cooperative_resolutions')->truncate();
                    DB::table('cooperative_documents')->truncate();
                    DB::table('cooperative_members')->truncate();
                    DB::table('organizations')->where('type', 'COOPERATIVE')->delete();
                    DB::statement('SET FOREIGN_KEY_CHECKS = 1');
                    break;
                case 'carts':
                    $affected = DB::table('carts')->count();
                    DB::statement('SET FOREIGN_KEY_CHECKS = 0');
                    DB::table('cart_items')->truncate();
                    DB::table('carts')->truncate();
                    DB::statement('SET FOREIGN_KEY_CHECKS = 1');
                    break;
                case 'farmer_profiles':
                    $affected = DB::table('farmer_profiles')->count();
                    DB::statement('SET FOREIGN_KEY_CHECKS = 0');
                    DB::table('farmer_profiles')->truncate();
                    DB::statement('SET FOREIGN_KEY_CHECKS = 1');
                    break;
                default:
                    return ResponseHelper::returnError('Invalid flush target');
            }
            return Response::json(['success' => true, 'message' => "Flushed $target successfully", 'affected' => $affected]);
        } catch (\Throwable $th) {
            return ResponseHelper::returnError($th->getMessage());
        }
    }

    /**
     * Trigger a test notification for a specific user.
     */
    public function testNotification(Request $request)
    {
        if (!$this->checkAccess()) return ResponseHelper::returnUnauthorized();

        $userHash = $request->input('user_hash');
        $user = User::where('hashkey', $userHash)->first();
        if (!$user) return ResponseHelper::returnError('User not found');

        // Setting exec_command which SSEController picks up to notify client
        $user->exec_command = 'toast:success:Test Notification from Ultimate Console: ' . date('H:i:s');
        $user->save();

        return Response::json(['success' => true]);
    }
    
    /**
     * Batch management for various entities.
     */
    public function batchManage(Request $request)
    {
        if (!$this->checkAccess() || !UserPermissions::isActionPermitted(0, UserActions::UltimateBatch)) {
            return ResponseHelper::returnUnauthorized();
        }
        
        $action = $request->input('action');
        $ids = $request->input('ids', []);
        $data = $request->input('data', []);
        
        if (empty($ids) && !in_array($action, ['cleanup_sessions'])) {
            return ResponseHelper::returnError('No IDs provided');
        }
        
        try {
            switch($action) {
                case 'activate_users':
                    User::whereIn('id', $ids)->update(['active' => true]);
                    break;
                case 'deactivate_users':
                    User::whereIn('id', $ids)->update(['active' => false]);
                    break;
                case 'cleanup_sessions':
                    DB::table('pos_sessions')->where('status', 'VOIDED')->delete();
                    break;
                case 'mass_transfer_points':
                    $amount = (float)($data['amount'] ?? 0);
                    if ($amount <= 0) return ResponseHelper::returnError('Invalid amount');
                    
                    foreach ($ids as $id) {
                        GlobalTransaction::create([
                            'user_id' => $id,
                            'amount' => $amount,
                            'type' => 'REWARD',
                            'description' => 'Mass points adjustment via Ultimate Console',
                            'is_active' => true,
                        ]);
                    }
                    break;
                default:
                    return ResponseHelper::returnError('Invalid batch action');
            }
            return Response::json(['success' => true]);
        } catch (\Throwable $th) {
            return ResponseHelper::returnError($th->getMessage());
        }
    }

    /**
     * Run a system command (Artisan wrapper).
     */
    public function runCommand(Request $request)
    {
        if (!$this->checkAccess()) return ResponseHelper::returnUnauthorized();

        $command = $request->input('command');
        if (empty($command)) return ResponseHelper::returnError('Command cannot be empty');

        // Normalize command: strip 'php artisan ' if present
        $command = preg_replace('/^php artisan\s+/', '', trim($command));

        // Mapping for user-friendly commands
        if ($command === 'reset-app all users') {
            $command = 'app:reset-users';
        }

        if ($command === 'db seed') {
            $command = 'db:seed';
        }

        // For security, only allow specific commands
        $allowedCommands = [
            'cache:clear', 'view:clear', 'config:clear', 'route:clear', 
            'migrate', 'migrate:rollback', 'migrate:fresh', 
            'db:seed', 'app:reset-users', 'optimize', 'optimize:clear'
        ];
        
        $baseCommand = explode(' ', trim($command))[0];

        if (!in_array($baseCommand, $allowedCommands)) {
            return ResponseHelper::returnError("Command '{$baseCommand}' not allowed for security reasons.");
        }

        try {
            // In Hyperf, running commands from HTTP request context is tricky.
            // We'll use shell_exec in this local environment demo as a fallback.
            $output = shell_exec("php artisan $command 2>&1");
            return Response::json(['success' => true, 'output' => $output]);
        } catch (\Throwable $th) {
            return ResponseHelper::returnError($th->getMessage());
        }
    }

    /**
     * Run `php artisan migrate --force` (non-interactive).
     */
    public function runMigrate(Request $request)
    {
        if (!$this->checkAccess()) return ResponseHelper::returnUnauthorized();

        try {
            $output = shell_exec('cd ' . escapeshellarg(BASE_PATH) . ' && php artisan migrate --force 2>&1');
            return Response::json(['success' => true, 'output' => $output]);
        } catch (\Throwable $th) {
            return ResponseHelper::returnError($th->getMessage());
        }
    }

    /**
     * Download a full database backup.
     * Puts system in maintenance mode during the process.
     */
    public function downloadBackup()
    {
        if (!$this->checkAccess()) return ResponseHelper::returnUnauthorized();

            // 1. Enable maintenance mode & Notify
            Redis::set('system:maintenance_mode', 'true');
            Redis::set('system:global_message', json_encode(['text' => 'System backup in progress. Transactions temporarily disabled.', 'type' => 'warning']));

            try {
                $filename = 'backup_' . date('Y-m-d_H-i-s') . '.sql';
                $path = BASE_PATH . '/storage/app/backups/' . $filename;
                
                if (!is_dir(dirname($path))) {
                    mkdir(dirname($path), 0755, true);
                }

                $dbHost = env('DB_HOST', '127.0.0.1');
                $dbPort = env('DB_PORT', '3306');
                $dbName = env('DB_DATABASE', 'bukid');
                $dbUser = env('DB_USERNAME', 'root');
                $dbPass = env('DB_PASSWORD', '');

                $dump = new \Ifsnop\Mysqldump\Mysqldump(
                    "mysql:host={$dbHost};port={$dbPort};dbname={$dbName}",
                    $dbUser,
                    $dbPass,
                    [
                        'add-drop-table' => true,
                        'exclude-tables' => ['db_backups'] // Exclude the backups table
                    ]
                );
                
                $dump->start($path);

                if (!file_exists($path) || filesize($path) === 0) {
                    throw new \Exception('Backup file was not created or is empty.');
                }

                // Compress into 7z Ultra
                $sevenZFilename = 'backup_' . date('Y-m-d_H-i-s') . '.7z';
                $sevenZPath = BASE_PATH . '/storage/app/backups/' . $sevenZFilename;
                
                // -mx=9 for Ultra compression
                $path_escaped = escapeshellarg($path);
                $sevenZPath_escaped = escapeshellarg($sevenZPath);
                $command = "7z a -t7z -m0=lzma2 -mx=9 {$sevenZPath_escaped} {$path_escaped} 2>&1";
                shell_exec($command);
                
                if (!file_exists($sevenZPath)) {
                    throw new \Exception('Failed to create 7z archive.');
                }
                
                // Save to database
                $fileContentRaw = file_get_contents($sevenZPath);
                $fileHash = hash('sha256', $fileContentRaw);
                
                $fileContent = new FileContent();
                $fileContent->filehash = $fileHash;
                $fileContent->titlename = $sevenZFilename;
                $fileContent->description = 'System database backup';
                $fileContent->size_in_bytes = filesize($sevenZPath);
                $fileContent->content = base64_encode($fileContentRaw);
                $fileContent->mimetype = 'application/x-7z-compressed';
                $fileContent->created_by = Auth::id();
                $fileContent->updated_by = Auth::id();
                $fileContent->save();

                $dbBackup = new DbBackup();
                $dbBackup->file_content_hashkey = $fileContent->hashkey;
                $dbBackup->filename = $sevenZFilename;
                $dbBackup->size_in_bytes = filesize($sevenZPath);
                $dbBackup->created_by = Auth::id();
                $dbBackup->updated_by = Auth::id();
                $dbBackup->save();

                // Clean up the temporary files from filesystem
                @unlink($path);
                @unlink($sevenZPath);

                // 2. Disable maintenance mode & Clear Notify
                Redis::set('system:maintenance_mode', 'false');
                Redis::del('system:global_message');

                // 3. Return the binary content for download
                return Response::make($fileContentRaw, 200, [
                    'Content-Type' => 'application/x-7z-compressed',
                    'Content-Disposition' => 'attachment; filename="' . $sevenZFilename . '"',
                    'Content-Length' => strlen($fileContentRaw),
                ]);
            } catch (\Throwable $th) {
                Redis::set('system:maintenance_mode', 'false');
                return ResponseHelper::returnError($th->getMessage());
            }
    }

    /**
     * Get recently created backups.
     */
    public function getBackups()
    {
        if (!$this->checkAccess()) return ResponseHelper::returnUnauthorized();

        $backups = DbBackup::with(['creator'])
            ->orderBy('created_at', 'desc')
            ->limit(50)
            ->get();

        return Response::json(['success' => true, 'data' => $backups]);
    }

    /**
     * Rename a specific backup.
     */
    public function renameBackup(Request $request)
    {
        if (!$this->checkAccess()) return ResponseHelper::returnUnauthorized();

        $hash = $request->input('hash');
        $newName = $request->input('name');
        
        if (empty($newName)) return ResponseHelper::returnError('Name cannot be empty');

        $backup = DbBackup::where('hashkey', $hash)->first();
        if (!$backup) return ResponseHelper::returnError('Backup not found');

        $backup->name = $newName;
        $backup->save();

        return Response::json(['success' => true]);
    }

    /**
     * Delete a specific backup.
     */
    public function deleteBackup(Request $request)
    {
        if (!$this->checkAccess()) return ResponseHelper::returnUnauthorized();

        $hash = $request->input('hash');
        $backup = DbBackup::where('hashkey', $hash)->first();
        if (!$backup) return ResponseHelper::returnError('Backup not found');

        // Delete associated file content
        FileContent::where('hashkey', $backup->file_content_hashkey)->delete();
        
        // Delete backup record
        $backup->delete();

        return Response::json(['success' => true]);
    }

    /**
     * Download a specific backup from the database.
     */
    public function downloadBackupByHash(Request $request)
    {
        if (!$this->checkAccess()) return ResponseHelper::returnUnauthorized();

        $hash = $request->input('hash');
        $backup = DbBackup::where('hashkey', $hash)->first();
        if (!$backup) return ResponseHelper::returnError('Backup not found');

        $fileContent = FileContent::where('hashkey', $backup->file_content_hashkey)->first();
        if (!$fileContent) return ResponseHelper::returnError('File content not found');

        $content = base64_decode($fileContent->content);
        
        return Response::make($content, 200, [
            'Content-Type' => $fileContent->mimetype,
            'Content-Disposition' => 'attachment; filename="' . $backup->filename . '"',
            'Content-Length' => strlen($content),
        ]);
    }


    /**
     * Get system-wide logs from file and database.
     */
    public function getSystemLogs(Request $request)
    {
        if (!$this->checkAccess()) return ResponseHelper::returnUnauthorized();

        $type = $request->input('type', 'database');
        
        if ($type === 'file') {
            $logPath = BASE_PATH . '/storage/logs/hypervel.log';
            if (!file_exists($logPath)) {
                return Response::json(['success' => true, 'data' => 'No file logs found.']);
            }
            $logs = shell_exec("tail -n 1000 " . escapeshellarg($logPath));
            return Response::json(['success' => true, 'data' => $logs]);
        }

        // Database logs (audit)
        if ($type === 'audit') {
            $logs = DB::table('table_logs')->orderBy('id', 'desc')->limit(500)->get();
            return Response::json(['success' => true, 'data' => $logs]);
        }

        // Database logs (system)
        $logs = DB::table('logs')->orderBy('uid', 'desc')->limit(500)->get();
        return Response::json(['success' => true, 'data' => $logs]);
    }
}
