<?php

declare(strict_types=1);

namespace ServerManager\Core;

class Logger
{
    private static ?Logger $instance = null;
    private string $logPath;
    private string $logLevel;
    private array $levels = [
        'debug' => 0,
        'info' => 1,
        'notice' => 2,
        'warning' => 3,
        'error' => 4,
        'critical' => 5,
        'alert' => 6,
        'emergency' => 7,
    ];

    private function __construct()
    {
    }

    public static function getInstance(): self
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public static function init(string $path, string $level = 'warning'): void
    {
        $instance = self::getInstance();
        $instance->logPath = rtrim($path, '/') . '/';
        $instance->logLevel = $level;

        if (!is_dir($instance->logPath)) {
            mkdir($instance->logPath, 0750, true);
        }

        if (!is_writable($instance->logPath)) {
            throw new \RuntimeException("Log path is not writable: {$instance->logPath}");
        }
    }

    public function log(string $level, string $message, array $context = []): void
    {
        if (!$this->shouldLog($level)) {
            return;
        }

        $timestamp = date('Y-m-d H:i:s');
        $contextJson = !empty($context) ? ' ' . json_encode($context, JSON_UNESCAPED_SLASHES) : '';
        $userInfo = $this->getUserInfo();

        $logEntry = sprintf(
            "[%s] %s.%s: %s%s %s\n",
            $timestamp,
            strtoupper($level),
            $userInfo,
            $message,
            $contextJson,
            $this->getRequestInfo()
        );

        $this->write($level, $logEntry);
    }

    public function debug(string $message, array $context = []): void
    {
        $this->log('debug', $message, $context);
    }

    public function info(string $message, array $context = []): void
    {
        $this->log('info', $message, $context);
    }

    public function notice(string $message, array $context = []): void
    {
        $this->log('notice', $message, $context);
    }

    public function warning(string $message, array $context = []): void
    {
        $this->log('warning', $message, $context);
    }

    public function error(string $message, array $context = []): void
    {
        $this->log('error', $message, $context);
    }

    public function critical(string $message, array $context = []): void
    {
        $this->log('critical', $message, $context);
    }

    public function alert(string $message, array $context = []): void
    {
        $this->log('alert', $message, $context);
    }

    public function emergency(string $message, array $context = []): void
    {
        $this->log('emergency', $message, $context);
    }

    private function shouldLog(string $level): bool
    {
        $currentLevel = $this->levels[$this->logLevel] ?? 3;
        $messageLevel = $this->levels[$level] ?? 3;
        return $messageLevel >= $currentLevel;
    }

    private function write(string $level, string $entry): void
    {
        $filename = $this->logPath . 'app-' . date('Y-m-d') . '.log';
        file_put_contents($filename, $entry, FILE_APPEND | LOCK_EX);

        if (in_array($level, ['error', 'critical', 'alert', 'emergency'], true)) {
            $errorFile = $this->logPath . 'error-' . date('Y-m-d') . '.log';
            file_put_contents($errorFile, $entry, FILE_APPEND | LOCK_EX);
        }
    }

    private function getUserInfo(): string
    {
        $userId = Session::get('user_id');
        $username = Session::get('username');
        if ($userId) {
            return "[User:{$userId}:{$username}]";
        }
        return '[Guest]';
    }

    private function getRequestInfo(): string
    {
        $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
        $method = $_SERVER['REQUEST_METHOD'] ?? 'CLI';
        $uri = $_SERVER['REQUEST_URI'] ?? '';
        return "[{$ip}] [{$method} {$uri}]";
    }
}
