<?php

/**
 * ============================================================
 *
 * @project   biiproject
 *
 * @author    Andika Debi Putra
 *
 * @email     andikadebiputra@gmail.com
 *
 * @website   https://biiproject.com
 *
 * @copyright Copyright (c) 2026 Andika Debi Putra
 * @license   Proprietary - All Rights Reserved
 *
 * @version   1.0.0
 *
 * @created   2026-05-01
 * ============================================================
 *
 * Unauthorized copying, modification, distribution, or use
 * of this file is strictly prohibited without prior written
 * permission from the author.
 * ============================================================
 */

namespace App\Http\Controllers\SystemSettings;

use App\Services\AI\SecurityHardeningService;
use App\Services\Monitoring\SystemMonitoringService;
use App\Support\DataTable;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Response;

class SystemMonitoringController extends Controller
{
    public function __construct(
        protected SystemMonitoringService $monitor
    ) {
        // Middleware handled in web.php
    }

    public function index()
    {
        if (! auth()->user()->can('view health and logs')) {
            abort(403);
        }
        $stats = $this->monitor->getAll();

        return view('pages.system_settings.system-monitoring', compact('stats'));
    }

    public function getStats(Request $request)
    {
        if (! $request->user()->can('view health and logs')) {
            return response()->json([]);
        }

        return response()->json($this->monitor->getAll());
    }

    public function logsDataTable(Request $request)
    {
        if (! $request->user()->can('view health and logs')) {
            return DataTable::response($request, 0, 0, []);
        }
        $logFile = storage_path('logs/laravel.log');
        if (! File::exists($logFile)) {
            return DataTable::response($request, 0, 0, []);
        }

        // Optimization: Don't read the whole file into memory. Use tail to get last 2000 lines.
        // This prevents memory exhaustion on large log files.
        $handle = shell_exec('tail -n 2000 '.escapeshellarg($logFile));
        $lines = $handle ? explode("\n", trim($handle)) : [];
        $logs = [];

        $currentLog = null;

        foreach ($lines as $line) {
            // Pattern for standard Laravel logs: [YYYY-MM-DD HH:MM:SS] env.LEVEL: message
            if (preg_match('/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] (.*?)\.(.*?): (.*)/', $line, $matches)) {
                if ($currentLog) {
                    $logs[] = $currentLog;
                }
                $currentLog = [
                    'timestamp' => $matches[1],
                    'env' => $matches[2],
                    'level' => strtolower($matches[3]),
                    'message' => $matches[4],
                ];
            } else {
                if ($currentLog) {
                    $currentLog['message'] .= "\n".$line;
                }
            }
        }
        if ($currentLog) {
            $logs[] = $currentLog;
        }

        $logs = array_reverse($logs); // Newest first

        $recordsTotal = count($logs);

        // Filter
        $searchValue = DataTable::globalSearch($request);
        if ($searchValue) {
            $searchValue = strtolower($searchValue);
            $logs = array_filter($logs, function ($log) use ($searchValue) {
                return str_contains(strtolower($log['timestamp']), $searchValue) ||
                    str_contains(strtolower($log['level']), $searchValue) ||
                    str_contains(strtolower($log['message']), $searchValue);
            });
        }

        $recordsFiltered = count($logs);

        // Sorting
        [$orderIndex, $orderDir] = DataTable::order($request, 0, 'desc');
        $sortKey = match ($orderIndex) {
            0 => 'timestamp',
            1 => 'level',
            2 => 'message',
            default => 'timestamp'
        };

        usort($logs, function ($a, $b) use ($sortKey, $orderDir) {
            if ($orderDir === 'asc') {
                return $a[$sortKey] <=> $b[$sortKey];
            }

            return $b[$sortKey] <=> $a[$sortKey];
        });

        // Pagination
        $data = array_slice($logs, DataTable::start($request), DataTable::length($request));

        $rows = array_map(function ($log) {
            $levelClass = match ($log['level']) {
                'error', 'critical', 'alert', 'emergency' => 'text-bg-danger',
                'warning' => 'text-bg-warning',
                'notice', 'info' => 'text-bg-info',
                'debug' => 'text-bg-light border',
                default => 'text-bg-secondary'
            };

            return [
                '<div class="text-nowrap">'.e($log['timestamp']).'</div>',
                '<span class="badge px-2 py-1 '.$levelClass.'">'.strtoupper(e($log['level'])).'</span>',
                '<div class="text-truncate small" style="max-width: 600px;" title="'.e($log['message']).'">'.e(str_replace("\n", ' ', substr($log['message'], 0, 300))).'</div>',
                '<div class="text-end">
                    <button class="btn btn-sm btn-outline-theme view-log-detail" data-log=\''.htmlspecialchars(json_encode($log), ENT_QUOTES, 'UTF-8').'\'>
                        <i class="bi bi-eye"></i>
                    </button>
                 </div>',
            ];
        }, $data);

        return DataTable::response($request, $recordsTotal, $recordsFiltered, $rows);
    }

    public function downloadLogs()
    {
        $logFile = storage_path('logs/laravel.log');
        if (! File::exists($logFile)) {
            return back()->with('error', 'Log file not found.');
        }

        return Response::download($logFile, 'laravel-'.now()->format('Y-m-d').'.log');
    }

    public function clearLogs()
    {
        $logFile = storage_path('logs/laravel.log');
        File::put($logFile, '');

        return response()->json(['success' => true, 'message' => 'Logs cleared successfully.']);
    }

    /**
     * SAP RFC LOGS DATATABLE
     */
    public function sapLogsDataTable(Request $request)
    {
        $possiblePaths = [
            base_path('dev_rfc.trc'),
            public_path('dev_rfc.trc'),
            storage_path('logs/dev_rfc.trc'),
        ];

        $logFile = null;
        foreach ($possiblePaths as $path) {
            if (File::exists($path)) {
                $logFile = $path;
                break;
            }
        }

        if (! $logFile) {
            return DataTable::response($request, 0, 0, []);
        }

        // Optimization: Use tail for large SAP logs
        $handle = shell_exec('tail -c 500000 '.escapeshellarg($logFile)); // Read last 500KB
        $content = $handle ?: '';
        $blocks = explode('****', $content);
        $logs = [];

        foreach ($blocks as $block) {
            $block = trim($block);
            if (empty($block)) {
                continue;
            }

            $timestamp = 'N/A';
            if (preg_match('/at (\d{8} \d{6})/', $block, $matches)) {
                $timestamp = Carbon::createFromFormat('Ymd His', $matches[1])->format('Y-m-d H:i:s');
            }

            // Extract a clean summary (e.g. "Trace file opened", "RFC Call", etc.)
            $summary = 'General RFC Event';
            if (preg_match('/Trace file opened/i', $block)) {
                $summary = 'SYSTEM: Trace Initialized';
            } elseif (preg_match('/RFC\s+Call\s+to\s+function\s+([\w_]+)/i', $block, $m)) {
                $summary = 'CALL: '.$m[1];
            } elseif (preg_match('/RFC\s+Call/i', $block)) {
                $summary = 'NETWORK: RFC Call Initiated';
            } elseif (preg_match('/Error/i', $block)) {
                $summary = 'ERROR: RFC Fault';
            } elseif (preg_match('/Connection/i', $block)) {
                $summary = 'CONN: State Changed';
            }

            $logs[] = [
                'timestamp' => $timestamp,
                'summary' => $summary,
                'message' => $block,
            ];
        }

        $logs = array_reverse($logs);
        $recordsTotal = count($logs);

        // Search
        $searchValue = DataTable::globalSearch($request);
        if ($searchValue) {
            $searchValue = strtolower($searchValue);
            $logs = array_filter($logs, function ($log) use ($searchValue) {
                return str_contains(strtolower($log['timestamp']), $searchValue) ||
                    str_contains(strtolower($log['summary']), $searchValue) ||
                    str_contains(strtolower($log['message']), $searchValue);
            });
        }

        $recordsFiltered = count($logs);
        $data = array_slice($logs, DataTable::start($request), DataTable::length($request));

        $rows = array_map(function ($log) {
            $summaryClass = 'text-primary';
            if (str_contains($log['summary'], 'ERROR')) {
                $summaryClass = 'text-danger fw-bold';
            }
            if (str_contains($log['summary'], 'SYSTEM')) {
                $summaryClass = 'text-success';
            }
            if (str_contains($log['summary'], 'CALL:')) {
                $summaryClass = 'text-info fw-bold';
            }

            return [
                '<div class="text-nowrap">'.e($log['timestamp']).'</div>',
                '<div class="'.$summaryClass.' small">'.e($log['summary']).'</div>',
                '<div class="text-end">
                    <button class="btn btn-sm btn-outline-theme view-sap-log-detail" data-message=\''.htmlspecialchars($log['message'], ENT_QUOTES, 'UTF-8').'\'>
                        <i class="bi bi-eye"></i>
                    </button>
                 </div>',
            ];
        }, $data);

        return DataTable::response($request, $recordsTotal, $recordsFiltered, $rows);
    }

    public function clearSapLogs()
    {
        $possiblePaths = [
            base_path('dev_rfc.trc'),
            public_path('dev_rfc.trc'),
            storage_path('logs/dev_rfc.trc'),
        ];

        foreach ($possiblePaths as $path) {
            if (File::exists($path)) {
                File::put($path, '');
            }
        }

        return response()->json(['success' => true, 'message' => 'SAP RFC logs cleared successfully.']);
    }

    public function downloadSapLogs()
    {
        $possiblePaths = [
            base_path('dev_rfc.trc'),
            public_path('dev_rfc.trc'),
            storage_path('logs/dev_rfc.trc'),
        ];

        foreach ($possiblePaths as $path) {
            if (File::exists($path)) {
                return Response::download($path, 'dev_rfc-'.now()->format('Y-m-d').'.trc');
            }
        }

        return back()->with('error', 'SAP log file not found.');
    }

    /**
     * MOBILE LOGS DATATABLE
     */
    public function mobileLogsDataTable(Request $request)
    {
        $logFile = storage_path('logs/mobile.log');
        if (! File::exists($logFile)) {
            return DataTable::response($request, 0, 0, []);
        }

        // Optimization: Get last 2000 lines only
        $handle = shell_exec('tail -n 2000 '.escapeshellarg($logFile));
        $lines = $handle ? explode("\n", trim($handle)) : [];
        $logs = [];
        $currentLog = null;

        foreach ($lines as $line) {
            if (preg_match('/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] (.*?)\.(.*?): (.*)/', $line, $matches)) {
                if ($currentLog) {
                    $logs[] = $currentLog;
                }
                $currentLog = [
                    'timestamp' => $matches[1],
                    'level' => strtolower($matches[3]),
                    'message' => $matches[4],
                ];
            } else {
                if ($currentLog) {
                    $currentLog['message'] .= "\n".$line;
                }
            }
        }
        if ($currentLog) {
            $logs[] = $currentLog;
        }

        $logs = array_reverse($logs);
        $recordsTotal = count($logs);

        // Search
        $searchValue = DataTable::globalSearch($request);
        if ($searchValue) {
            $searchValue = strtolower($searchValue);
            $logs = array_filter($logs, function ($log) use ($searchValue) {
                return str_contains(strtolower($log['timestamp']), $searchValue) ||
                    str_contains(strtolower($log['message']), $searchValue);
            });
        }

        $recordsFiltered = count($logs);
        $data = array_slice($logs, DataTable::start($request), DataTable::length($request));

        $rows = array_map(function ($log) {
            $levelClass = match ($log['level']) {
                'error', 'critical' => 'text-bg-danger',
                'warning' => 'text-bg-warning',
                default => 'text-bg-info'
            };

            // Try to extract user info from context if it was logged as JSON
            $userInfo = 'Guest';
            if (preg_match('/Context: (\{.*\})/s', $log['message'], $matches)) {
                $ctx = json_decode($matches[1], true);
                if (isset($ctx['user_id'])) {
                    $userInfo = 'ID: '.$ctx['user_id'];
                }
            }

            return [
                '<div class="text-nowrap">'.e($log['timestamp']).'</div>',
                '<span class="badge px-2 py-1 '.$levelClass.'">'.strtoupper(e($log['level'])).'</span>',
                '<div class="small fw-bold text-theme-1">'.e($userInfo).'</div>',
                '<div class="text-truncate small" style="max-width: 500px;">'.e(str_replace("\n", ' ', substr($log['message'], 0, 300))).'</div>',
                '<div class="text-end">
                    <button class="btn btn-sm btn-outline-theme view-log-detail" data-log=\''.htmlspecialchars(json_encode($log), ENT_QUOTES, 'UTF-8').'\'>
                        <i class="bi bi-eye"></i>
                    </button>
                 </div>',
            ];
        }, $data);

        return DataTable::response($request, $recordsTotal, $recordsFiltered, $rows);
    }

    public function clearMobileLogs()
    {
        File::put(storage_path('logs/mobile.log'), '');

        return response()->json(['success' => true, 'message' => 'Mobile logs cleared successfully.']);
    }

    public function downloadMobileLogs()
    {
        $logFile = storage_path('logs/mobile.log');
        if (! File::exists($logFile)) {
            return back()->with('error', 'Mobile log file not found.');
        }

        return Response::download($logFile, 'mobile-'.now()->format('Y-m-d').'.log');
    }

    /**
     * UNIFIED BACKGROUND JOBS DATATABLE
     */
    public function backgroundJobsDataTable(Request $request)
    {
        $canManage = $request->user()->can('manage health and logs');
        $pendingJobs = DB::table('jobs')->get()->map(function ($j) {
            $j->status = 'PENDING';
            $j->timestamp = Carbon::createFromTimestamp($j->available_at)->format('Y-m-d H:i:s');
            $j->exception = '';

            return $j;
        });

        $failedJobs = DB::table('failed_jobs')->get()->map(function ($j) {
            $j->status = 'FAILED';
            $j->timestamp = is_numeric($j->failed_at) ? Carbon::createFromTimestamp($j->failed_at)->format('Y-m-d H:i:s') : $j->failed_at;

            return $j;
        });

        $allJobs = collect($pendingJobs)->merge($failedJobs);
        $recordsTotal = $allJobs->count();

        // Search
        $searchValue = DataTable::globalSearch($request);
        if ($searchValue) {
            $searchValue = strtolower($searchValue);
            $allJobs = $allJobs->filter(function ($j) use ($searchValue) {
                return str_contains(strtolower($j->id), $searchValue) ||
                    str_contains(strtolower($j->queue), $searchValue) ||
                    str_contains(strtolower($j->status), $searchValue) ||
                    str_contains(strtolower($j->payload), $searchValue) ||
                    str_contains(strtolower($j->exception), $searchValue);
            });
        }
        $recordsFiltered = $allJobs->count();

        // Sort
        [$orderIndex, $orderDir] = DataTable::order($request, 0, 'desc');
        $sortKey = match ($orderIndex) {
            0 => 'timestamp',
            1 => 'status',
            2 => 'queue',
            default => 'timestamp'
        };

        if ($orderDir === 'asc') {
            $allJobs = $allJobs->sortBy($sortKey);
        } else {
            $allJobs = $allJobs->sortByDesc($sortKey);
        }

        // Paginate
        $allJobs = $allJobs->slice(DataTable::start($request), DataTable::length($request));

        $rows = $allJobs->map(function ($job) use ($canManage) {
            $payload = json_decode($job->payload);
            $displayName = $payload->displayName ?? 'Unknown Job';

            $statusBadge = $job->status === 'PENDING'
                ? '<span class="badge px-2 py-1 text-bg-warning">PENDING</span>'
                : '<span class="badge px-2 py-1 text-bg-danger">FAILED</span>';

            $details = '';
            if ($job->status === 'FAILED') {
                $exceptionShort = substr($job->exception, 0, 100);
                if (preg_match('/^([^\s:]+):/', $job->exception, $matches)) {
                    $exceptionClass = explode('\\', $matches[1]);
                    $exceptionClass = end($exceptionClass);
                } else {
                    $exceptionClass = 'Error';
                }
                $details = '<div class="text-truncate small" style="max-width: 400px;" title="'.e($job->exception).'">
                    <strong class="text-danger">'.e($exceptionClass).'</strong>: '.e(str_replace("\n", ' ', $exceptionShort)).'...
                 </div>';
            } else {
                $details = '<div class="text-truncate small" style="max-width: 400px;" title="'.e($displayName).'">
                    <strong class="text-dark">'.e($displayName).'</strong>
                 </div>';
            }

            // 🚀 HUMAN-READABLE DESCRIPTION MAPPING (TEXT ONLY)
            $taskDescription = match (true) {
                str_contains($displayName, 'SystemManagementNotification') => 'Mengirim Notifikasi Manajemen Sistem',
                str_contains($displayName, 'BroadcastEvent') => 'Penyiaran Data Real-time (Websocket)',
                str_contains($displayName, 'Backup') => 'Menjalankan Pencadangan Sistem',
                str_contains($displayName, 'Cleanup') => 'Pembersihan Data Berkala',
                str_contains($displayName, 'Sync') => 'Sinkronisasi Data Eksternal',
                default => 'Tugas Sistem Latar Belakang'
            };

            $row = [
                '<div class="text-nowrap">'.e($job->timestamp).'</div>',
                $statusBadge,
                '<span class="badge text-bg-secondary">'.strtoupper(e($job->queue)).'</span>',
                $details,
                '<div class="small fw-bold text-theme-1">'.$taskDescription.'</div>',
            ];

            if ($canManage) {
                $actions = '<div class="text-end d-flex justify-content-end gap-1">';
                if ($job->status === 'FAILED') {
                    $actions .= '<button class="btn btn-sm btn-outline-theme retry-job-btn" data-id="'.$job->id.'" title="Retry Job"><i class="bi bi-arrow-repeat"></i></button>';
                    $actions .= '<button class="btn btn-sm btn-outline-theme view-job-detail" data-job=\''.htmlspecialchars(json_encode($job), ENT_QUOTES, 'UTF-8').'\' title="View Detail"><i class="bi bi-eye"></i></button>';
                    $actions .= '<button class="btn btn-sm btn-outline-danger delete-failed-job-btn" data-id="'.$job->id.'" title="Delete Failed Job"><i class="bi bi-trash"></i></button>';
                } else {
                    // Pending jobs cannot be deleted individually as per user request
                    $actions .= '<button class="btn btn-sm btn-outline-secondary" disabled title="Pending job"><i class="bi bi-hourglass-split"></i></button>';
                }
                $actions .= '</div>';
                $row[] = $actions;
            }

            return $row;
        })->values()->all();

        return DataTable::response($request, $recordsTotal, $recordsFiltered, $rows);
    }

    /**
     * CLEAR ALL PENDING AND FAILED JOBS
     */
    public function clearFailedJobs()
    {
        // 1. Clear Failed Jobs
        Artisan::call('queue:flush');

        // 2. Clear Pending Jobs
        DB::table('jobs')->delete();

        return response()->json(['success' => true, 'message' => 'All pending and failed jobs have been cleared.']);
    }

    /**
     * RETRY FAILED JOB(S)
     */
    public function retryFailedJob(Request $request, $id = null)
    {
        if ($id) {
            Artisan::call('queue:retry', ['id' => [$id]]);
            $msg = "Job #{$id} has been pushed back to the queue.";
        } else {
            Artisan::call('queue:retry', ['id' => ['all']]);
            $msg = 'All failed jobs have been pushed back to the queue.';
        }

        return response()->json(['success' => true, 'message' => $msg]);
    }

    /**
     * DELETE SINGLE FAILED JOB
     */
    public function deleteFailedJob($id)
    {
        DB::table('failed_jobs')->where('id', $id)->delete();

        return response()->json(['success' => true, 'message' => 'Failed job deleted successfully.']);
    }

    /**
     * DELETE SINGLE PENDING JOB
     */
    public function deletePendingJob($id)
    {
        DB::table('jobs')->where('id', $id)->delete();

        return response()->json(['success' => true, 'message' => 'Pending job deleted successfully.']);
    }

    /**
     * DOWNLOAD BACKGROUND JOBS (Pending & Failed)
     */
    public function downloadBackgroundJobs(Request $request)
    {
        $format = $request->get('format', 'csv');
        $delimiter = $format === 'excel' ? "\t" : ',';
        $filename = 'background-jobs-'.now()->format('Y-m-d-His').'.'.($format === 'excel' ? 'xls' : 'csv');

        return response()->streamDownload(function () use ($delimiter) {
            $file = fopen('php://output', 'w');

            // Add BOM for Excel UTF-8 support
            fprintf($file, chr(0xEF).chr(0xBB).chr(0xBF));

            $columns = ['ID', 'Type', 'Status', 'Queue', 'Timestamp', 'Message (Payload/Exception)'];
            fputcsv($file, $columns, $delimiter);

            // Pending Jobs
            DB::table('jobs')->orderBy('available_at', 'desc')->chunk(100, function ($jobs) use ($file, $delimiter) {
                foreach ($jobs as $job) {
                    $payload = json_decode($job->payload);
                    fputcsv($file, [
                        $job->id,
                        'Pending Job',
                        'PENDING',
                        $job->queue,
                        Carbon::createFromTimestamp($job->available_at)->toDateTimeString(),
                        $payload->displayName ?? 'Unknown',
                    ], $delimiter);
                }
            });

            // Failed Jobs
            DB::table('failed_jobs')->orderBy('failed_at', 'desc')->chunk(100, function ($jobs) use ($file, $delimiter) {
                foreach ($jobs as $job) {
                    fputcsv($file, [
                        $job->id,
                        'Failed Job',
                        'FAILED',
                        $job->queue,
                        $job->failed_at,
                        $job->exception,
                    ], $delimiter);
                }
            });

            fclose($file);
        }, $filename, [
            'Content-Type' => $format === 'excel' ? 'application/vnd.ms-excel' : 'text/csv',
        ]);
    }

    /**
     * TOGGLE MAINTENANCE MODE
     */
    public function toggleMaintenance(Request $request)
    {
        $status = $request->get('status'); // true = down, false = up

        try {
            if ($status) {
                Artisan::call('down', [
                    '--refresh' => 15,
                    '--secret' => 'debug-access',
                ]);
                $msg = 'System is now in MAINTENANCE mode.';
            } else {
                Artisan::call('up');
                $msg = 'System is now LIVE.';
            }

            return response()->json([
                'success' => true,
                'message' => $msg,
                'is_down' => app()->isDownForMaintenance(),
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Failed to toggle maintenance mode: '.$e->getMessage(),
            ], 500);
        }
    }

    /**
     * AI SECURITY AUDIT
     */
    public function securityAudit(Request $request, SecurityHardeningService $service)
    {
        if (! $request->user()->can('view ai log analysis')) {
            return response()->json(['error' => 'Unauthorized'], 403);
        }

        if ($request->has('refresh')) {
            Cache::forget('security_audit_result');
        }

        $result = $service->auditSecurity();

        return response()->json($result);
    }
}
