<?php
/**
 * Clase AuditLog - Sistema de Auditoría
 * Registra todos los cambios en tablas críticas del sistema SSOMA
 */

require_once __DIR__ . '/../config/database.php';

class AuditLog {
    private $pdo;

    public function __construct() {
        $this->pdo = getDB();
    }

    /**
     * Registrar cambio en log de auditoría
     *
     * @param string $tabla Nombre de la tabla modificada
     * @param int $registro_id ID del registro modificado
     * @param string $accion Tipo de operación (CREATE, UPDATE, DELETE)
     * @param mixed $valor_anterior Valores antes del cambio
     * @param mixed $valor_nuevo Valores después del cambio
     * @param string $usuario Usuario que realizó el cambio
     * @param string $modulo Módulo origen del cambio
     * @return bool True si se registró exitosamente
     */
    public function log($tabla, $registro_id, $accion, $valor_anterior = null, $valor_nuevo = null, $usuario = 'sistema', $modulo = null) {
        try {
            $sql = "INSERT INTO AuditLog
                (tabla, registro_id, accion, valor_anterior, valor_nuevo, usuario, modulo, ip_address, fecha_hora)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW())";

            $stmt = $this->pdo->prepare($sql);

            // Obtener IP del cliente
            $ip_address = $this->getClientIP();

            $result = $stmt->execute([
                $tabla,
                $registro_id,
                strtoupper($accion),
                $valor_anterior ? json_encode($valor_anterior, JSON_UNESCAPED_UNICODE) : null,
                $valor_nuevo ? json_encode($valor_nuevo, JSON_UNESCAPED_UNICODE) : null,
                $usuario,
                $modulo,
                $ip_address
            ]);

            return $result;

        } catch (PDOException $e) {
            error_log("Error en AuditLog: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Obtener historial de cambios para un registro específico
     *
     * @param string $tabla Nombre de la tabla
     * @param int $registro_id ID del registro
     * @param int $limit Cantidad de registros a obtener
     * @return array Lista de cambios
     */
    public function getHistorial($tabla, $registro_id, $limit = 50) {
        try {
            $sql = "SELECT * FROM AuditLog
            WHERE tabla = ? AND registro_id = ?
            ORDER BY fecha_hora DESC
            LIMIT ?";

            $stmt = $this->pdo->prepare($sql);
            $stmt->execute([$tabla, $registro_id, $limit]);

            return $stmt->fetchAll();

        } catch (PDOException $e) {
            error_log("Error al obtener historial: " . $e->getMessage());
            return [];
        }
    }

    /**
     * Obtener todos los cambios de un usuario
     *
     * @param string $usuario Nombre del usuario
     * @param int $limit Cantidad de registros
     * @return array Lista de cambios
     */
    public function getHistorialUsuario($usuario, $limit = 100) {
        try {
            $sql = "SELECT * FROM AuditLog
            WHERE usuario = ?
            ORDER BY fecha_hora DESC
            LIMIT ?";

            $stmt = $this->pdo->prepare($sql);
            $stmt->execute([$usuario, $limit]);

            return $stmt->fetchAll();

        } catch (PDOException $e) {
            error_log("Error al obtener historial de usuario: " . $e->getMessage());
            return [];
        }
    }

    /**
     * Obtener cambios recientes del sistema
     *
     * @param int $horas Cantidad de horas hacia atrás
     * @param int $limit Cantidad de registros
     * @return array Lista de cambios recientes
     */
    public function getCambiosRecientes($horas = 24, $limit = 100) {
        try {
            $sql = "SELECT * FROM AuditLog
            WHERE fecha_hora >= DATE_SUB(NOW(), INTERVAL ? HOUR)
            ORDER BY fecha_hora DESC
            LIMIT ?";

            $stmt = $this->pdo->prepare($sql);
            $stmt->execute([$horas, $limit]);

            return $stmt->fetchAll();

        } catch (PDOException $e) {
            error_log("Error al obtener cambios recientes: " . $e->getMessage());
            return [];
        }
    }

    /**
     * Obtener estadísticas de auditoría
     *
     * @return array Estadísticas de cambios por tipo
     */
    public function getEstadisticas() {
        try {
            $sql = "SELECT
                accion,
                COUNT(*) as total,
                COUNT(DISTINCT usuario) as usuarios_unicos
            FROM AuditLog
            WHERE fecha_hora >= DATE_SUB(NOW(), INTERVAL 30 DAY)
            GROUP BY accion";

            $stmt = $this->pdo->query($sql);
            return $stmt->fetchAll();

        } catch (PDOException $e) {
            error_log("Error al obtener estadísticas: " . $e->getMessage());
            return [];
        }
    }

    /**
     * Obtener dirección IP del cliente
     *
     * @return string IP del cliente
     */
    private function getClientIP() {
        $ip = 'UNKNOWN';

        if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
        } elseif (!empty($_SERVER['REMOTE_ADDR'])) {
            $ip = $_SERVER['REMOTE_ADDR'];
        }

        return $ip;
    }

    /**
     * Comparar dos arrays y obtener diferencias
     * Útil para auditoría de cambios
     *
     * @param array $old Valores antiguos
     * @param array $new Valores nuevos
     * @return array Diferencias encontradas
     */
    public static function getDiferencias($old, $new) {
        $diferencias = [];

        foreach ($new as $key => $value) {
            if (!isset($old[$key]) || $old[$key] !== $value) {
                $diferencias[$key] = [
                    'anterior' => $old[$key] ?? null,
                    'nuevo' => $value
                ];
            }
        }

        return $diferencias;
    }
}
?>
