<?php

declare(strict_types=1);

namespace ServerManager\Services;

use ServerManager\Core\Database;
use ServerManager\Core\Session;
use ServerManager\Core\Logger;

class AuditService
{
    private Database $db;
    private Logger $logger;

    public function __construct()
    {
        $this->db = Database::getInstance();
        $this->logger = Logger::getInstance();
    }

    public function log(
        string $action,
        string $entityType,
        ?int $entityId,
        ?array $details = null,
        ?array $metadata = null
    ): void {
        $userId = Session::get('user_id');
        $username = Session::get('username');
        $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
        $userAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'unknown';
        $now = date('Y-m-d H:i:s');

        $this->db->insert('audit_logs', [
            'user_id' => $userId,
            'username' => $username ?? 'system',
            'action' => $action,
            'entity_type' => $entityType,
            'entity_id' => $entityId,
            'details' => $details ? json_encode($details, JSON_UNESCAPED_SLASHES) : null,
            'ip_address' => $ip,
            'user_agent' => $userAgent,
            'created_at' => $now,
        ]);

        $logMessage = sprintf(
            '[AUDIT] %s | %s | %s:%s | User: %s (%d)',
            $action,
            $entityType,
            $entityId ?? 'N/A',
            $ip,
            $username ?? 'system',
            $userId ?? 0
        );

        $this->logger->info($logMessage);

        if (in_array($action, ['credential_access', 'credential_decrypt', 'credential_use'])) {
            $this->logger->warning("[CREDENTIAL_ACCESS] {$logMessage}");
        }
    }

    public function logCredentialAccess(int $serverId, string $reason): void
    {
        $server = $this->db->fetch('SELECT name, ip_address FROM servers WHERE id = ?', [$serverId]);
        $serverName = $server['name'] ?? 'Unknown';

        $this->log(
            'credential_access',
            'server_credentials',
            $serverId,
            [
                'server_name' => $serverName,
                'ip_address' => $server['ip_address'] ?? 'unknown',
                'reason' => $reason,
                'timestamp' => date('c'),
            ]
        );
    }

    public function logLogin(string $username, bool $success, ?string $reason = null): void
    {
        $this->log(
            $success ? 'login_success' : 'login_failed',
            'user',
            null,
            [
                'username' => $username,
                'success' => $success,
                'reason' => $reason,
            ]
        );

        if (!$success) {
            $this->logger->warning("[SECURITY] Failed login attempt for username: {$username}", [
                'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            ]);
        }
    }

    public function logServerAction(int $serverId, string $action, string $command): void
    {
        $this->log(
            'server_action',
            'server',
            $serverId,
            [
                'action' => $action,
                'command' => $command,
            ]
        );
    }

    public function logUserManagement(int $userId, string $action): void
    {
        $this->log(
            $action,
            'user',
            $userId,
            [
                'performed_by' => Session::get('username'),
            ]
        );
    }

    public function getRecentActivity(int $limit = 20, ?int $userId = null): array
    {
        $sql = 'SELECT al.*, u.username as actor_name 
                FROM audit_logs al 
                LEFT JOIN users u ON al.user_id = u.id';
        $params = [];

        if ($userId !== null) {
            $sql .= ' WHERE al.user_id = ?';
            $params[] = $userId;
        }

        $sql .= ' ORDER BY al.created_at DESC LIMIT ?';
        $params[] = $limit;

        return $this->db->fetchAll($sql, $params);
    }

    public function getAuditLogs(int $page = 1, int $perPage = 50, array $filters = []): array
    {
        $where = [];
        $params = [];

        if (!empty($filters['action'])) {
            $where[] = 'al.action = ?';
            $params[] = $filters['action'];
        }

        if (!empty($filters['user_id'])) {
            $where[] = 'al.user_id = ?';
            $params[] = $filters['user_id'];
        }

        if (!empty($filters['username'])) {
            $where[] = '(al.username LIKE ? OR u.username LIKE ?)';
            $params[] = '%' . $filters['username'] . '%';
            $params[] = '%' . $filters['username'] . '%';
        }

        if (!empty($filters['entity_type'])) {
            $where[] = 'al.entity_type = ?';
            $params[] = $filters['entity_type'];
        }

        if (!empty($filters['ip_address'])) {
            $where[] = 'al.ip_address LIKE ?';
            $params[] = '%' . $filters['ip_address'] . '%';
        }

        if (!empty($filters['details'])) {
            $where[] = 'al.details LIKE ?';
            $params[] = '%' . $filters['details'] . '%';
        }

        if (!empty($filters['date_from'])) {
            $where[] = 'al.created_at >= ?';
            $params[] = $filters['date_from'];
        }

        if (!empty($filters['date_to'])) {
            $where[] = 'al.created_at <= ?';
            $params[] = $filters['date_to'];
        }

        $whereClause = !empty($where) ? 'WHERE ' . implode(' AND ', $where) : '';
        $offset = ($page - 1) * $perPage;

        $total = $this->db->fetch(
            "SELECT COUNT(*) as total FROM audit_logs al {$whereClause}",
            $params
        );

        $logs = $this->db->fetchAll(
            "SELECT al.*, u.username as actor_name 
             FROM audit_logs al 
             LEFT JOIN users u ON al.user_id = u.id 
             {$whereClause} 
             ORDER BY al.created_at DESC 
             LIMIT ? OFFSET ?",
            array_merge($params, [$perPage, $offset])
        );

        return [
            'data' => $logs,
            'total' => (int) ($total['total'] ?? 0),
            'page' => $page,
            'per_page' => $perPage,
            'total_pages' => ceil((int) ($total['total'] ?? 0) / $perPage),
        ];
    }
}
