<?php

namespace SSCI\Classes;

/**
 * AuditLog Class
 * Tracks all user actions for compliance, security, and debugging
 * 
 * Logs:
 * - CRUD operations (Create, Read, Update, Delete)
 * - User authentication events
 * - Settings changes
 * - Permission changes
 * - Payment transactions
 * - Document downloads/uploads
 * - System-level actions
 */
class AuditLog
{
    private $db;
    private static $staticDb = null;

    // Action types
    const ACTION_CREATE = 'CREATE';
    const ACTION_READ = 'READ';
    const ACTION_UPDATE = 'UPDATE';
    const ACTION_DELETE = 'DELETE';
    const ACTION_LOGIN = 'LOGIN';
    const ACTION_LOGOUT = 'LOGOUT';
    const ACTION_EXPORT = 'EXPORT';
    const ACTION_IMPORT = 'IMPORT';
    const ACTION_PERMISSION_CHANGE = 'PERMISSION_CHANGE';
    const ACTION_SETTINGS_CHANGE = 'SETTINGS_CHANGE';
    const ACTION_PAYMENT = 'PAYMENT';
    const ACTION_DOWNLOAD = 'DOWNLOAD';
    const ACTION_UPLOAD = 'UPLOAD';
    const ACTION_BULK_UPDATE = 'BULK_UPDATE';
    const ACTION_SEND_NOTIFICATION = 'SEND_NOTIFICATION';

    public function __construct($db = null)
    {
        $this->db = $db ?? self::$staticDb;
    }

    /**
     * Set static database for convenience methods
     */
    public static function setStatic($db)
    {
        self::$staticDb = $db;
    }

    /**
     * Log an action
     * 
     * @param string $action Action type (use class constants)
     * @param string $entity Entity type (e.g., 'users', 'invoices', 'payments')
     * @param mixed $entityId Entity ID
     * @param string $description Human-readable description of the action
     * @param int|null $userId User performing the action (default: current user)
     * @param array $changes Array of ['field' => ['old' => value, 'new' => value]]
     * @return bool Success status
     */
    public function log($action, $entity, $entityId, $description, $userId = null, $changes = null)
    {
        try {
            $userId = $userId ?? ($_SESSION['user_id'] ?? null);
            
            // Prepare old_values and new_values from changes array
            $oldValues = null;
            $newValues = null;
            if ($changes) {
                if (isset($changes['old'])) {
                    $oldValues = json_encode($changes['old']);
                }
                if (isset($changes['new'])) {
                    $newValues = json_encode($changes['new']);
                }
            }
            
            return $this->db->query(
                "INSERT INTO audit_logs 
                (user_id, action, table_name, record_id, old_values, new_values, ip_address, user_agent, created_at)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW())",
                [
                    $userId,
                    $action,
                    $entity,
                    $entityId,
                    $oldValues,
                    $newValues,
                    $_SERVER['REMOTE_ADDR'] ?? 'CLI',
                    $_SERVER['HTTP_USER_AGENT'] ?? 'CLI',
                ]
            );
        } catch (\Exception $e) {
            error_log("AuditLog error: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Convenience method: Log create action
     */
    public static function logCreate($entity, $entityId, $description, $data = null, $userId = null)
    {
        $log = new self();
        return $log->log(self::ACTION_CREATE, $entity, $entityId, $description, $userId, $data);
    }

    /**
     * Convenience method: Log update action with change tracking
     */
    public static function logUpdate($entity, $entityId, $description, $oldValues = null, $newValues = null, $userId = null)
    {
        $log = new self();
        $changes = null;
        
        if ($oldValues && $newValues) {
            $changes = [];
            foreach ($newValues as $field => $newVal) {
                if (!isset($oldValues[$field]) || $oldValues[$field] !== $newVal) {
                    $changes[$field] = [
                        'old' => $oldValues[$field] ?? null,
                        'new' => $newVal
                    ];
                }
            }
        }
        
        return $log->log(self::ACTION_UPDATE, $entity, $entityId, $description, $userId, $changes);
    }

    /**
     * Convenience method: Log delete action
     */
    public static function logDelete($entity, $entityId, $description, $userId = null)
    {
        $log = new self();
        return $log->log(self::ACTION_DELETE, $entity, $entityId, $description, $userId);
    }

    /**
     * Convenience method: Log authentication event
     */
    public static function logLogin($userId, $success = true)
    {
        $log = new self();
        $action = $success ? self::ACTION_LOGIN : 'LOGIN_FAILED';
        $description = $success ? 'User logged in' : 'Failed login attempt';
        return $log->log($action, 'users', $userId, $description, $userId);
    }

    /**
     * Convenience method: Log logout event
     */
    public static function logLogout($userId)
    {
        $log = new self();
        return $log->log(self::ACTION_LOGOUT, 'users', $userId, 'User logged out', $userId);
    }

    /**
     * Convenience method: Log payment transaction
     */
    public static function logPayment($paymentId, $amount, $method, $status = 'success', $userId = null)
    {
        $log = new self();
        $description = "Payment of $amount via $method - Status: $status";
        return $log->log(self::ACTION_PAYMENT, 'payments', $paymentId, $description, $userId);
    }

    /**
     * Convenience method: Log settings change
     */
    public static function logSettingsChange($setting, $oldValue, $newValue, $userId = null)
    {
        $log = new self();
        $changes = [$setting => ['old' => $oldValue, 'new' => $newValue]];
        return $log->log(
            self::ACTION_SETTINGS_CHANGE,
            'settings',
            $setting,
            "Settings changed: $setting",
            $userId,
            $changes
        );
    }

    /**
     * Convenience method: Log notification sent
     */
    public static function logNotificationSent($userId, $notificationType, $channels = [])
    {
        $log = new self();
        $description = "Notification sent via: " . implode(', ', $channels);
        return $log->log(
            self::ACTION_SEND_NOTIFICATION,
            'notifications',
            null,
            $description,
            $userId,
            ['type' => $notificationType, 'channels' => $channels]
        );
    }

    /**
     * Get audit logs with filtering
     * 
     * @param array $filters Array of filters: ['user_id' => 123, 'action' => 'CREATE', 'entity' => 'users', 'limit' => 50]
     * @return array Audit log records
     */
    public function getAuditLogs($filters = [])
    {
        try {
            $sql = "SELECT * FROM audit_logs WHERE 1=1";
            $params = [];

            if (!empty($filters['user_id'])) {
                $sql .= " AND user_id = ?";
                $params[] = $filters['user_id'];
            }

            if (!empty($filters['action'])) {
                $sql .= " AND action = ?";
                $params[] = $filters['action'];
            }

            if (!empty($filters['entity'])) {
                $sql .= " AND entity = ?";
                $params[] = $filters['entity'];
            }

            if (!empty($filters['entity_id'])) {
                $sql .= " AND entity_id = ?";
                $params[] = $filters['entity_id'];
            }

            if (!empty($filters['date_from'])) {
                $sql .= " AND DATE(created_at) >= ?";
                $params[] = $filters['date_from'];
            }

            if (!empty($filters['date_to'])) {
                $sql .= " AND DATE(created_at) <= ?";
                $params[] = $filters['date_to'];
            }

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

            return $this->db->fetchAll($sql, $params);
        } catch (\Exception $e) {
            error_log("Error getting audit logs: " . $e->getMessage());
            return [];
        }
    }

    /**
     * Get audit summary statistics
     */
    public function getAuditSummary($days = 30)
    {
        try {
            $sql = "
                SELECT 
                    action,
                    COUNT(*) as total,
                    COUNT(DISTINCT user_id) as unique_users,
                    MAX(created_at) as last_occurrence
                FROM audit_logs
                WHERE created_at >= DATE_SUB(NOW(), INTERVAL ? DAY)
                GROUP BY action
                ORDER BY total DESC
            ";

            return $this->db->fetchAll($sql, [$days]);
        } catch (\Exception $e) {
            error_log("Error getting audit summary: " . $e->getMessage());
            return [];
        }
    }

    /**
     * Export audit logs as CSV
     */
    public function exportAuditLogs($filters = [])
    {
        $logs = $this->getAuditLogs($filters);
        
        $csv = "User ID,Action,Entity,Entity ID,Description,Changes,IP Address,User Agent,Created At\n";
        
        foreach ($logs as $log) {
            $csv .= sprintf(
                "\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"\n",
                $log['user_id'] ?? '',
                $log['action'],
                $log['entity'],
                $log['entity_id'] ?? '',
                str_replace('"', '""', $log['description']),
                str_replace('"', '""', $log['changes'] ?? ''),
                $log['ip_address'],
                str_replace('"', '""', $log['user_agent']),
                $log['created_at']
            );
        }

        return $csv;
    }

    /**
     * Delete old audit logs (retention policy)
     * 
     * @param int $daysToKeep Number of days of logs to retain (default: 90)
     */
    public function pruneOldLogs($daysToKeep = 90)
    {
        try {
            return $this->db->query(
                "DELETE FROM audit_logs
                WHERE created_at < DATE_SUB(NOW(), INTERVAL ? DAY)",
                [$daysToKeep]
            );
        } catch (\Exception $e) {
            error_log("Error pruning audit logs: " . $e->getMessage());
            return false;
        }
    }
}
