<?php

declare(strict_types=1);

namespace ServerManager\Controllers;

use ServerManager\Core\App;
use ServerManager\Core\Session;
use ServerManager\Models\Server;
use ServerManager\Models\User;
use ServerManager\Services\MonitoringService;
use ServerManager\Services\SSHService;
use ServerManager\Services\AuditService;
use ServerManager\Models\CommandHistory;
use ServerManager\Core\Validator;

class ApiController
{
    private \ServerManager\Core\View $view;

    public function __construct()
    {
        $this->view = App::getInstance()->getView();
    }

    private function authenticateRequest(): ?array
    {
        $token = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
        $token = str_replace('Bearer ', '', $token);

        if (empty($token) && isset($_GET['api_token'])) {
            $token = $_GET['api_token'];
        }

        if (empty($token)) {
            $this->view->json(['error' => 'API token is required'], 401);
        }

        $userModel = new User();
        $user = $userModel->findByApiToken($token);

        if (!$user) {
            $this->view->json(['error' => 'Invalid API token'], 401);
        }

        return $user;
    }

    public function status(): void
    {
        $user = $this->authenticateRequest();

        $serverModel = new Server();
        $accessibleIds = $serverModel->getAccessibleServerIds((int) $user['id']);

        $monitoringService = new MonitoringService();
        $stats = $monitoringService->getDashboardStats($accessibleIds);

        $this->view->json([
            'success' => true,
            'data' => [
                'application' => 'ServerManager',
                'version' => '1.0.0',
                'stats' => $stats,
                'timestamp' => date('c'),
            ],
        ]);
    }

    public function servers(): void
    {
        $user = $this->authenticateRequest();

        $serverModel = new Server();
        $page = (int) ($_GET['page'] ?? 1);
        $perPage = (int) ($_GET['per_page'] ?? 20);
        $search = $_GET['search'] ?? '';

        $filters = [];
        if ($search) $filters['search'] = $search;

        $accessibleIds = $serverModel->getAccessibleServerIds((int) $user['id']);
        $filters['accessible_ids'] = !empty($accessibleIds) ? $accessibleIds : [-1];

        $servers = $serverModel->getAll($page, $perPage, $filters);

        $this->view->json([
            'success' => true,
            'data' => $servers['data'],
            'pagination' => [
                'page' => $servers['page'],
                'per_page' => $servers['per_page'],
                'total' => $servers['total'],
                'total_pages' => $servers['total_pages'],
            ],
        ]);
    }

    public function serverDetail(int $id): void
    {
        $user = $this->authenticateRequest();

        $serverModel = new Server();
        $server = $serverModel->findById($id);

        if (!$server) {
            $this->view->json(['error' => 'Server not found'], 404);
        }

        if (!$serverModel->canView($id, (int) $user['id'])) {
            $this->view->json(['error' => 'Server not found'], 404);
        }

        $monitoringService = new MonitoringService();
        $metrics = $monitoringService->getLatestMetrics($id);

        unset($server['agent_key'], $server['ssh_password'], $server['ssh_key']);

        $this->view->json([
            'success' => true,
            'data' => [
                'server' => $server,
                'metrics' => $metrics,
            ],
        ]);
    }

    public function serverAdd(): void
    {
        $user = $this->authenticateRequest();

        $input = json_decode(file_get_contents('php://input'), true) ?? $_POST;

        $validator = new Validator();
        $rules = [
            'name' => 'required|min:2|max:100',
            'ip_address' => 'required',
            'ssh_port' => 'required|numeric|port',
            'ssh_user' => 'required|min:2|max:50',
        ];

        if (!$validator->validate($input, $rules)) {
            $this->view->json(['error' => $validator->getFirstError()], 400);
        }

        $serverModel = new Server();
        $id = $serverModel->create($input);

        $this->view->json([
            'success' => true,
            'message' => 'Server created successfully.',
            'server_id' => $id,
        ], 201);
    }

    public function serverUpdate(int $id): void
    {
        $user = $this->authenticateRequest();

        $input = json_decode(file_get_contents('php://input'), true) ?? $_POST;

        $serverModel = new Server();
        $server = $serverModel->findById($id);

        if (!$server) {
            $this->view->json(['error' => 'Server not found'], 404);
        }

        if (!$serverModel->canManage($id, (int) $user['id'])) {
            $this->view->json(['error' => 'Forbidden'], 403);
        }

        $serverModel->update($id, $input);

        $this->view->json([
            'success' => true,
            'message' => 'Server updated successfully.',
        ]);
    }

    public function serverDelete(int $id): void
    {
        $user = $this->authenticateRequest();

        $serverModel = new Server();
        $server = $serverModel->findById($id);

        if (!$server) {
            $this->view->json(['error' => 'Server not found'], 404);
        }

        if (!$serverModel->canManage($id, (int) $user['id'])) {
            $this->view->json(['error' => 'Forbidden'], 403);
        }

        $serverModel->delete($id);

        $this->view->json([
            'success' => true,
            'message' => 'Server deleted successfully.',
        ]);
    }

    public function serverMetrics(int $id): void
    {
        $user = $this->authenticateRequest();

        $serverModel = new Server();
        if (!$serverModel->canView($id, (int) $user['id'])) {
            $this->view->json(['error' => 'Server not found'], 404);
        }

        $monitoringService = new MonitoringService();

        $hours = (int) ($_GET['hours'] ?? 24);
        if ($hours > 168) $hours = 168;

        $metrics = $monitoringService->getHistoricalMetrics($id, $hours);

        $this->view->json([
            'success' => true,
            'data' => $metrics,
        ]);
    }

    public function executeCommand(int $id): void
    {
        $user = $this->authenticateRequest();

        $serverModel = new Server();
        if (!$serverModel->canView($id, (int) $user['id'])) {
            $this->view->json(['error' => 'Server not found'], 404);
        }

        $input = json_decode(file_get_contents('php://input'), true) ?? $_POST;
        $command = $input['command'] ?? '';

        if (empty($command)) {
            $this->view->json(['error' => 'Command is required'], 400);
        }

        $serverModel = new Server();
        $server = $serverModel->findById($id);

        if (!$server) {
            $this->view->json(['error' => 'Server not found'], 404);
        }

        try {
            $ssh = new SSHService();
            if (!$ssh->connect($server)) {
                throw new \RuntimeException('Could not establish SSH connection');
            }
            $result = $ssh->exec($command);
            $ssh->disconnect();

            $commandHistory = new CommandHistory();
            $commandHistory->log(
                $id,
                $user['id'],
                $command,
                $result['output'],
                $result['exit_status'] ?? 0
            );

            $auditService = new AuditService();
            $auditService->logServerAction($id, 'api_command_executed', $command);

            $this->view->json([
                'success' => true,
                'data' => [
                    'output' => $result['output'],
                    'exit_status' => $result['exit_status'],
                    'stderr' => $result['stderr'],
                ],
            ]);
        } catch (\Throwable $e) {
            $this->view->json([
                'error' => 'Command execution failed: ' . $e->getMessage(),
            ], 500);
        }
    }

    public function users(): void
    {
        $currentUser = $this->authenticateRequest();

        if ($currentUser['role'] !== 'super_admin' && $currentUser['role'] !== 'admin') {
            $this->view->json(['error' => 'Forbidden'], 403);
        }

        $userModel = new User();
        $users = $userModel->getAll(1, 100);

        $this->view->json([
            'success' => true,
            'data' => $users['data'],
        ]);
    }

    public function pushMetrics(): void
    {
        $input = json_decode(file_get_contents('php://input'), true);

        if (!$input || empty($input['agent_key'])) {
            $this->view->json(['error' => 'agent_key is required'], 401);
        }

        $serverModel = new Server();
        $server = $serverModel->findByAgentKey($input['agent_key']);

        if (!$server || !$server['is_active']) {
            $this->view->json(['error' => 'Invalid agent key'], 401);
        }

        $cpu = isset($input['cpu']) ? max(0, min(100, (float) $input['cpu'])) : 0;
        $ram = isset($input['ram']) ? max(0, min(100, (float) $input['ram'])) : 0;
        $disk = isset($input['disk']) ? max(0, min(100, (float) $input['disk'])) : 0;
        $load = (float) ($input['load'] ?? 0);
        $uptime = (string) ($input['uptime'] ?? '');

        $monitoring = new MonitoringService();
        $monitoring->saveMetrics([
            'server_id' => (int) $server['id'],
            'status' => 'online',
            'cpu_usage' => $cpu,
            'ram_usage' => $ram,
            'disk_usage' => $disk,
            'load_average' => $load,
            'uptime' => $uptime,
        ]);

        $serverModel->updateAgentLastSeen((int) $server['id']);

        $this->view->json(['success' => true]);
    }

    public function refreshAllMetrics(): void
    {
        $user = $this->authenticateRequest();

        $monitoringService = new MonitoringService();
        $results = $monitoringService->collectAllMetrics();

        $this->view->json([
            'success' => true,
            'data' => $results,
        ]);
    }

    public function register(): void
    {
        $input = json_decode(file_get_contents('php://input'), true) ?? $_POST;

        $validator = new Validator();
        $rules = [
            'username' => 'required|min:3|max:50',
            'email' => 'required|email',
            'password' => 'required|min:8',
            'confirm_password' => 'required',
        ];

        if (!$validator->validate($input, $rules)) {
            $this->view->json([
                'success' => false,
                'error' => $validator->getFirstError(),
            ], 400);
            return;
        }

        if ($input['password'] !== $input['confirm_password']) {
            $this->view->json([
                'success' => false,
                'error' => 'Passwords do not match.',
            ], 400);
            return;
        }

        $userModel = new User();

        $existing = $userModel->findByUsername($input['username']);
        if ($existing) {
            $this->view->json([
                'success' => false,
                'error' => 'Username is already taken.',
            ], 409);
            return;
        }

        $existingEmail = $userModel->findByEmail($input['email']);
        if ($existingEmail) {
            $this->view->json([
                'success' => false,
                'error' => 'Email is already registered.',
            ], 409);
            return;
        }

        $userId = $userModel->create([
            'username' => $input['username'],
            'email' => $input['email'],
            'password' => $input['password'],
            'role' => 'admin',
            'is_active' => 1,
        ]);

        $user = $userModel->findById($userId);

        $auditService = new AuditService();
        $auditService->log('user_registered', 'User registered via API', $userId);

        $this->view->json([
            'success' => true,
            'data' => [
                'token' => $user['api_token'],
                'user' => [
                    'id' => (int) $user['id'],
                    'username' => $user['username'],
                    'email' => $user['email'],
                    'role' => $user['role'],
                ],
            ],
        ], 201);
    }

    public function profile(): void
    {
        $user = $this->authenticateRequest();

        $this->view->json([
            'success' => true,
            'data' => [
                'id' => (int) $user['id'],
                'username' => $user['username'],
                'email' => $user['email'],
                'role' => $user['role'],
                'created_at' => $user['created_at'],
            ],
        ]);
    }

    public function updateProfile(): void
    {
        $user = $this->authenticateRequest();

        $input = json_decode(file_get_contents('php://input'), true) ?? $_POST;

        $validator = new Validator();
        $rules = [
            'email' => 'required|email',
        ];

        if (!$validator->validate($input, $rules)) {
            $this->view->json([
                'success' => false,
                'error' => $validator->getFirstError(),
            ], 400);
            return;
        }

        $updateData = ['email' => $input['email']];

        if (!empty($input['new_password'])) {
            if (strlen($input['new_password']) < 8) {
                $this->view->json([
                    'success' => false,
                    'error' => 'New password must be at least 8 characters.',
                ], 400);
                return;
            }
            if (empty($input['current_password'])) {
                $this->view->json([
                    'success' => false,
                    'error' => 'Current password is required to set a new password.',
                ], 400);
                return;
            }
            if ($input['new_password'] !== ($input['confirm_password'] ?? '')) {
                $this->view->json([
                    'success' => false,
                    'error' => 'New passwords do not match.',
                ], 400);
                return;
            }

            $userModel = new User();
            $fullUser = $userModel->findById((int) $user['id']);
            if (!$fullUser || !(new \ServerManager\Core\Security())->verifyPassword($input['current_password'], $fullUser['password_hash'])) {
                $this->view->json([
                    'success' => false,
                    'error' => 'Current password is incorrect.',
                ], 403);
                return;
            }

            $updateData['password'] = $input['new_password'];
        }

        $userModel = new User();
        $userModel->update((int) $user['id'], $updateData);

        $auditService = new AuditService();
        $auditService->log('profile_updated', 'Profile updated via API', (int) $user['id']);

        $this->view->json([
            'success' => true,
            'message' => 'Profile updated successfully.',
        ]);
    }

    public function appVersion(): void
    {
        $db = \ServerManager\Core\Database::getInstance();

        try {
            $configs = $db->fetchAll('SELECT * FROM app_config');
        } catch (\Throwable $e) {
            $this->view->json([
                'success' => false,
                'error' => 'Database error',
            ], 500);
            return;
        }

        $config = [];
        foreach ($configs as $row) {
            $config[$row['key']] = $row['value'];
        }

        $this->view->json([
            'success' => true,
            'data' => [
                'version' => $config['app_version'] ?? '1.0.0',
                'apk_url' => $config['apk_url'] ?? '/sysadmin.apk',
                'force_update' => ($config['force_update'] ?? '0') === '1',
                'release_notes' => $config['release_notes'] ?? '',
            ],
        ]);
    }

    public function unreadCount(): void
    {
        $user = $this->authenticateRequest();

        $notificationModel = new \ServerManager\Models\Notification();
        $count = $notificationModel->getUnreadCount((int) $user['id']);

        $this->view->json([
            'success' => true,
            'unread_count' => $count,
        ]);
    }

    public function notifications(): void
    {
        $user = $this->authenticateRequest();

        $notificationModel = new \ServerManager\Models\Notification();
        $page = (int) ($_GET['page'] ?? 1);
        $perPage = (int) ($_GET['per_page'] ?? 20);

        $result = $notificationModel->getForUser((int) $user['id'], $page, $perPage);
        $unreadCount = $notificationModel->getUnreadCount((int) $user['id']);

        $this->view->json([
            'success' => true,
            'data' => $result['data'],
            'pagination' => [
                'page' => $result['page'],
                'per_page' => $result['per_page'],
                'total' => $result['total'],
                'total_pages' => $result['total_pages'],
            ],
            'unread_count' => $unreadCount,
        ]);
    }

    public function markNotificationRead(int $id): void
    {
        $user = $this->authenticateRequest();

        $notificationModel = new \ServerManager\Models\Notification();
        $notificationModel->markAsRead($id, (int) $user['id']);

        $this->view->json(['success' => true]);
    }

    public function markAllNotificationsRead(): void
    {
        $user = $this->authenticateRequest();

        $notificationModel = new \ServerManager\Models\Notification();
        $notificationModel->markAllAsRead((int) $user['id']);

        $this->view->json(['success' => true]);
    }

    public function serverServices(int $id): void
    {
        $user = $this->authenticateRequest();

        $serverModel = new Server();
        if (!$serverModel->canView($id, (int) $user['id'])) {
            $this->view->json(['error' => 'Server not found'], 404);
        }

        $server = $serverModel->findById($id);
        if (!$server) {
            $this->view->json(['error' => 'Server not found'], 404);
        }

        try {
            $ssh = new SSHService();
            if (!$ssh->connect($server)) {
                throw new \RuntimeException('SSH connection failed');
            }

            $output = $ssh->exec('systemctl list-units --type=service --all --no-pager --no-legend 2>/dev/null');
            $ssh->disconnect();

            $services = [];
            if ($output['exit_status'] === 0) {
                foreach (explode("\n", trim($output['output'])) as $line) {
                    if (preg_match('/^\s*([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)/', $line, $m)) {
                        $services[] = [
                            'name' => trim($m[1]),
                            'load' => trim($m[2]),
                            'status' => trim($m[3]),
                            'description' => trim($m[4] ?? ''),
                        ];
                    }
                }
            }

            $this->view->json([
                'success' => true,
                'data' => $services,
            ]);
        } catch (\Throwable $e) {
            $this->view->json([
                'success' => false,
                'error' => $e->getMessage(),
            ], 500);
        }
    }

    public function serverReboot(int $id): void
    {
        $this->executeServerAction($id, 'reboot', '/sbin/reboot');
    }

    public function serverShutdown(int $id): void
    {
        $this->executeServerAction($id, 'shutdown', '/sbin/shutdown -h now');
    }

    public function serverTestConnection(int $id): void
    {
        $user = $this->authenticateRequest();

        $serverModel = new Server();
        if (!$serverModel->canView($id, (int) $user['id'])) {
            $this->view->json(['error' => 'Server not found'], 404);
        }

        $server = $serverModel->findById($id);
        if (!$server) {
            $this->view->json(['error' => 'Server not found'], 404);
        }

        try {
            $ssh = new SSHService();
            $connected = $ssh->connect($server);
            if ($connected) {
                $ssh->disconnect();
            }
            $this->view->json([
                'success' => true,
                'data' => ['connected' => $connected],
            ]);
        } catch (\Throwable $e) {
            $this->view->json([
                'success' => true,
                'data' => ['connected' => false, 'error' => $e->getMessage()],
            ]);
        }
    }

    public function serverServiceAction(int $id): void
    {
        $user = $this->authenticateRequest();

        $serverModel = new Server();
        if (!$serverModel->canManage($id, (int) $user['id'])) {
            $this->view->json(['error' => 'Forbidden'], 403);
        }

        $server = $serverModel->findById($id);
        if (!$server) {
            $this->view->json(['error' => 'Server not found'], 404);
        }

        $input = json_decode(file_get_contents('php://input'), true) ?? $_POST;
        $service = (string) ($input['service'] ?? '');
        $action = (string) ($input['action'] ?? '');
        $allowed = ['start', 'stop', 'restart', 'reload'];

        if ($service === '' || !in_array($action, $allowed, true)) {
            $this->view->json(['error' => 'Invalid service or action'], 400);
        }

        $sanitized = preg_replace('/[^a-zA-Z0-9\-_.]/', '', $service);
        if ($sanitized === '') {
            $this->view->json(['error' => 'Invalid service name'], 400);
        }

        try {
            $ssh = new SSHService();
            if (!$ssh->connect($server)) {
                throw new \RuntimeException('SSH connection failed');
            }

            $result = $ssh->exec("sudo systemctl {$action} {$sanitized} 2>&1");
            $ssh->disconnect();

            $exitCode = isset($result['exit_status']) ? (int) $result['exit_status'] : -1;

            $auditService = new AuditService();
            $auditService->logServerAction($id, "service_{$action}", "{$sanitized} {$action}");

            if ($exitCode === 0) {
                $this->view->json([
                    'success' => true,
                    'data' => [
                        'message' => "Service '{$sanitized}' {$action}ed.",
                        'output' => trim($result['output'] ?? ''),
                    ],
                ]);
            } else {
                $this->view->json([
                    'success' => false,
                    'error' => "Failed to {$action} '{$sanitized}'. Exit code: {$exitCode}",
                    'data' => ['output' => trim($result['output'] ?? '')],
                ], 500);
            }
        } catch (\Throwable $e) {
            $this->view->json([
                'success' => false,
                'error' => $e->getMessage(),
            ], 500);
        }
    }

    private function executeServerAction(int $id, string $action, string $command): void
    {
        $user = $this->authenticateRequest();

        $serverModel = new Server();
        if (!$serverModel->canManage($id, (int) $user['id'])) {
            $this->view->json(['error' => 'Forbidden'], 403);
        }

        $server = $serverModel->findById($id);
        if (!$server) {
            $this->view->json(['error' => 'Server not found'], 404);
        }

        try {
            $ssh = new SSHService();
            if (!$ssh->connect($server)) {
                throw new \RuntimeException('SSH connection failed');
            }
            $result = $ssh->exec($command);
            $ssh->disconnect();

            $auditService = new AuditService();
            $auditService->log("server_{$action}", 'server', $id, ['name' => $server['name']]);

            $this->view->json([
                'success' => true,
                'message' => ucfirst($action) . ' command executed.',
                'data' => [
                    'output' => $result['output'],
                    'exit_status' => $result['exit_status'],
                ],
            ]);
        } catch (\Throwable $e) {
            $this->view->json([
                'success' => false,
                'error' => $e->getMessage(),
            ], 500);
        }
    }

    public function login(): void
    {
        $input = json_decode(file_get_contents('php://input'), true) ?? $_POST;

        $validator = new Validator();
        $rules = [
            'username' => 'required',
            'password' => 'required|min:6',
        ];

        if (!$validator->validate($input, $rules)) {
            $this->view->json([
                'success' => false,
                'error' => $validator->getFirstError(),
            ], 400);
            return;
        }

        $userModel = new User();
        $user = $userModel->authenticate($input['username'], $input['password']);

        if (!$user) {
            $auditService = new AuditService();
            $auditService->logLogin($input['username'], false, 'Invalid credentials');
            $this->view->json([
                'success' => false,
                'error' => 'Invalid username or password.',
            ], 401);
            return;
        }

        $token = $user['api_token'];
        if (empty($token)) {
            $security = new \ServerManager\Core\Security();
            $token = $security->generateApiToken();
            $userModel->update((int) $user['id'], ['api_token' => $token]);
        }

        $auditService = new AuditService();
        $auditService->logLogin($input['username'], true);

        $this->view->json([
            'success' => true,
            'data' => [
                'token' => $token,
                'user' => [
                    'id' => (int) $user['id'],
                    'username' => $user['username'],
                    'email' => $user['email'],
                    'role' => $user['role'],
                ],
            ],
        ]);
    }
}
