<?php

namespace Gestoreasy\Core\Audit;

use Gestoreasy\Core\Tenant\ConnectionManager;
use Gestoreasy\Core\Config\Config;
use PDO;

class AuditLogger
{
    public static function log(
        string $action,
        string $resource,
        ?int $resourceId = null,
        ?array $oldData = null,
        ?array $newData = null,
        ?array $metadata = null
    ): void {
        try {
            $pdo = ConnectionManager::forTenant();
            $userId = isset($metadata['user_id']) ? (int)$metadata['user_id'] : null;
            
            $stmt = $pdo->prepare("
                INSERT INTO audit_logs (
                    user_id, action, resource, resource_id, 
                    old_data, new_data, metadata, created_at
                ) VALUES (?, ?, ?, ?, ?, ?, ?, NOW())
            ");
            
            $stmt->execute([
                $userId,
                $action,
                $resource,
                $resourceId,
                $oldData ? json_encode($oldData) : null,
                $newData ? json_encode($newData) : null,
                $metadata ? json_encode($metadata) : null
            ]);
        } catch (\Exception $e) {
            // Log de auditoria não deve quebrar a aplicação
            error_log("Audit log failed: " . $e->getMessage());
        }
    }
    
    public static function logCreate(string $resource, int $resourceId, array $data, ?array $metadata = null): void
    {
        self::log('create', $resource, $resourceId, null, $data, $metadata);
    }
    
    public static function logUpdate(string $resource, int $resourceId, array $oldData, array $newData, ?array $metadata = null): void
    {
        // Calcular apenas os campos que mudaram
        $changes = [];
        foreach ($newData as $key => $value) {
            if (!isset($oldData[$key]) || $oldData[$key] !== $value) {
                $changes[$key] = ['old' => $oldData[$key] ?? null, 'new' => $value];
            }
        }
        
        if (!empty($changes)) {
            self::log('update', $resource, $resourceId, $oldData, $newData, array_merge($metadata ?? [], ['changes' => $changes]));
        }
    }
    
    public static function logDelete(string $resource, int $resourceId, array $data, ?array $metadata = null): void
    {
        self::log('delete', $resource, $resourceId, $data, null, $metadata);
    }
    
    public static function logRead(string $resource, ?int $resourceId = null, ?array $metadata = null): void
    {
        // Log de leitura opcional (pode gerar muito volume)
        if (self::shouldLogReads()) {
            self::log('read', $resource, $resourceId, null, null, $metadata);
        }
    }
    
    public static function logLogin(int $userId, bool $success, ?array $metadata = null): void
    {
        $defaultMetadata = [
            'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown'
        ];
        
        self::log(
            $success ? 'login_success' : 'login_failed',
            'auth',
            $userId,
            null,
            null,
            array_merge($defaultMetadata, $metadata ?? [])
        );
    }
    
    public static function getAuditTrail(string $resource, ?int $resourceId = null, int $limit = 50): array
    {
        $pdo = ConnectionManager::forTenant();
        
        $sql = "
            SELECT al.*, u.email as user_email
            FROM audit_logs al
            LEFT JOIN users u ON al.user_id = u.id
            WHERE al.resource = ?
        ";
        
        $params = [$resource];
        
        if ($resourceId !== null) {
            $sql .= " AND al.resource_id = ?";
            $params[] = $resourceId;
        }
        
        // MySQL/MariaDB não aceitam placeholder em LIMIT com prepares reais.
        // Garantir que o limite seja um inteiro seguro e injetá-lo diretamente.
        $safeLimit = max(1, (int)$limit);
        $sql .= " ORDER BY al.created_at DESC LIMIT " . $safeLimit;
        
        $stmt = $pdo->prepare($sql);
        $stmt->execute($params);
        
        $logs = $stmt->fetchAll(PDO::FETCH_ASSOC);
        
        // Decodificar JSON
        foreach ($logs as &$log) {
            $log['old_data'] = $log['old_data'] ? json_decode($log['old_data'], true) : null;
            $log['new_data'] = $log['new_data'] ? json_decode($log['new_data'], true) : null;
            $log['metadata'] = $log['metadata'] ? json_decode($log['metadata'], true) : null;
        }
        
        return $logs;
    }
    
    private static function shouldLogReads(): bool
    {
        return (bool) Config::get('AUDIT_LOG_READS', false);
    }

    public static function pruneOlderThan(int $days): int
    {
        try {
            $pdo = ConnectionManager::forTenant();
            $stmt = $pdo->prepare('DELETE FROM audit_logs WHERE created_at < (NOW() - INTERVAL ? DAY)');
            $stmt->execute([$days]);
            return $stmt->rowCount();
        } catch (\Exception $e) {
            error_log("Audit prune failed: " . $e->getMessage());
            return 0;
        }
    }
}