<?php

namespace App\Services;


/**
 * Servicio de logging
 * Registra eventos y errores del sistema
 */
class Logger
{
    private const LOG_LEVELS = [
        'DEBUG' => 0,
        'INFO' => 1,
        'WARNING' => 2,
        'ERROR' => 3,
        'CRITICAL' => 4
    ];

    private Database $db;
    private string $logFile;
    private string $minLevel;

    public function __construct()
    {
        $this->db = Database::getInstance();
        $this->logFile = storage_path('logs/app.log');
        $this->minLevel = $_ENV['APP_ENV'] === 'production' ? 'WARNING' : 'DEBUG';

        // Crear directorio de logs si no existe
        $logDir = dirname($this->logFile);
        if (!is_dir($logDir)) {
            mkdir($logDir, 0755, true);
        }
    }

    /**
     * Registrar log en archivo
     */
    private function writeToFile(string $level, string $message, array $context = []): void
    {
        if (self::LOG_LEVELS[$level] < self::LOG_LEVELS[$this->minLevel]) {
            return;
        }

        $timestamp = gmdate('Y-m-d H:i:s');
        $contextStr = !empty($context) ? ' | Context: ' . json_encode($context) : '';

        $logLine = "[{$timestamp}] [{$level}] {$message}{$contextStr}" . PHP_EOL;

        file_put_contents($this->logFile, $logLine, FILE_APPEND | LOCK_EX);
    }

    /**
     * Registrar log en base de datos
     */
    private function writeToDatabase(
        string $tipo,
        string $accion,
        string $descripcion = '',
        ?int $userId = null,
        ?int $contractId = null,
        string $nivel = 'info',
        array $detalles = []
    ): void {
        try {
            $this->db->insert('access_logs', [
                'tipo' => $tipo,
                'usuario_id' => $userId,
                'contract_id' => $contractId,
                'accion' => $accion,
                'descripcion' => $descripcion,
                'ip_address' => $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1',
                'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
                'detalles' => !empty($detalles) ? json_encode($detalles) : null,
                'nivel' => strtolower($nivel)
            ]);
        } catch (\Exception $e) {
            // Si falla el log en BD, solo escribir en archivo
            $this->writeToFile('ERROR', 'Failed to write to database log: ' . $e->getMessage());
        }
    }

    /**
     * Log de debug
     */
    public function debug(string $message, array $context = []): void
    {
        $this->writeToFile('DEBUG', $message, $context);
    }

    /**
     * Log de info
     */
    public function info(string $message, array $context = []): void
    {
        $this->writeToFile('INFO', $message, $context);
    }

    /**
     * Log de warning
     */
    public function warning(string $message, array $context = []): void
    {
        $this->writeToFile('WARNING', $message, $context);
    }

    /**
     * Log de error
     */
    public function error(string $message, array $context = []): void
    {
        $this->writeToFile('ERROR', $message, $context);
    }

    /**
     * Log de crítico
     */
    public function critical(string $message, array $context = []): void
    {
        $this->writeToFile('CRITICAL', $message, $context);
    }

    /**
     * Log de acceso de admin
     */
    public function logAdminAccess(int $adminId, string $accion, string $descripcion = '', array $detalles = []): void
    {
        $this->writeToDatabase('admin', $accion, $descripcion, $adminId, null, 'info', $detalles);
    }

    /**
     * Log de firma
     */
    public function logSignature(int $contractId, string $accion, string $descripcion = '', array $detalles = []): void
    {
        $this->writeToDatabase('firma', $accion, $descripcion, null, $contractId, 'info', $detalles);
    }

    /**
     * Log de seguridad
     */
    public function logSecurity(string $accion, string $descripcion, string $nivel = 'warning', array $detalles = []): void
    {
        $this->writeToDatabase('security', $accion, $descripcion, null, null, $nivel, $detalles);
        $this->writeToFile(strtoupper($nivel), "SECURITY: {$accion} - {$descripcion}", $detalles);
    }

    /**
     * Log de API
     */
    public function logApi(string $endpoint, string $method, int $statusCode, array $detalles = []): void
    {
        $nivel = $statusCode >= 500 ? 'error' : ($statusCode >= 400 ? 'warning' : 'info');
        $this->writeToDatabase('api', "{$method} {$endpoint}", "Status: {$statusCode}", null, null, $nivel, $detalles);
    }

    /**
     * Leer últimos logs del archivo
     */
    public function getRecentLogs(int $lines = 100): array
    {
        if (!file_exists($this->logFile)) {
            return [];
        }

        $file = new \SplFileObject($this->logFile);
        $file->seek(PHP_INT_MAX);
        $totalLines = $file->key() + 1;

        $startLine = max(0, $totalLines - $lines);
        $logs = [];

        $file->seek($startLine);
        while (!$file->eof()) {
            $line = trim($file->current());
            if (!empty($line)) {
                $logs[] = $line;
            }
            $file->next();
        }

        return $logs;
    }

    /**
     * Limpiar logs antiguos
     */
    public function cleanOldLogs(int $days = 30): void
    {
        $cutoffDate = gmdate('Y-m-d H:i:s', time() - ($days * 86400));

        try {
            $this->db->query(
                "DELETE FROM access_logs WHERE created_at < :cutoff",
                [':cutoff' => $cutoffDate]
            );

            $this->info("Cleaned logs older than {$days} days");
        } catch (\Exception $e) {
            $this->error("Failed to clean old logs: " . $e->getMessage());
        }
    }
}
