<?php
/**
 * Klasa Logger - obsługa logów
 */

class Logger
{
    private Database $db;
    private ?string $logsIpColumn = null;
    private ?string $apiLogsIpColumn = null;
    
    public function __construct()
    {
        $this->db = Database::getInstance();
        $this->detectColumnNames();
    }
    
    /**
     * Wykryj nazwy kolumn IP (kompatybilność ze starszymi bazami)
     */
    private function detectColumnNames(): void
    {
        // Sprawdź kolumnę IP w tabeli logs
        try {
            $result = $this->db->fetchAll("PRAGMA table_info(logs)");
            foreach ($result as $col) {
                if ($col['name'] === 'ip_address') {
                    $this->logsIpColumn = 'ip_address';
                    break;
                } elseif ($col['name'] === 'ip') {
                    $this->logsIpColumn = 'ip';
                }
            }
        } catch (Exception $e) {
            $this->logsIpColumn = 'ip';
        }
        
        // Sprawdź kolumnę IP w tabeli api_logs
        try {
            $result = $this->db->fetchAll("PRAGMA table_info(api_logs)");
            foreach ($result as $col) {
                if ($col['name'] === 'ip_address') {
                    $this->apiLogsIpColumn = 'ip_address';
                    break;
                } elseif ($col['name'] === 'ip') {
                    $this->apiLogsIpColumn = 'ip';
                }
            }
        } catch (Exception $e) {
            $this->apiLogsIpColumn = 'ip';
        }
    }

    /**
     * Loguj akcję użytkownika do bazy i pliku
     */
    public function log(string $action, ?string $details = null, ?int $userId = null): void
    {
        // Zapis do bazy danych
        try {
            $ipCol = $this->logsIpColumn ?? 'ip';
            $this->db->insert(
                "INSERT INTO logs (user_id, action, details, {$ipCol}, user_agent, created_at) 
                 VALUES (:user_id, :action, :details, :ip, :user_agent, datetime('now'))",
                [
                    'user_id' => $userId,
                    'action' => $action,
                    'details' => $details,
                    'ip' => $this->getClientIp(),
                    'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? null
                ]
            );
        } catch (Exception $e) {
            // Jeśli błąd bazy, zapisz do pliku z informacją o błędzie
            $this->logToFile('app.log', "[DB_ERROR] " . $e->getMessage());
        }
        
        // Zapis do pliku
        $this->logToFile('app.log', "[{$action}] " . ($details ?? '') . " (user: " . ($userId ?? 'system') . ")");
    }

    /**
     * Loguj request API
     */
    public function logApi(?int $deviceId, ?string $mac, ?string $code = null, string $endpoint = '', string $method = 'GET', int $responseCode = 200, int $playlistsCount = 0, int $responseTimeMs = 0): void
    {
        try {
            $ipCol = $this->apiLogsIpColumn ?? 'ip';
            
            // Sprawdź które kolumny istnieją w api_logs
            $columns = ['device_id', 'mac', 'code', $ipCol];
            $values = [':device_id', ':mac', ':code', ':ip'];
            $params = [
                'device_id' => $deviceId,
                'mac' => $mac,
                'code' => $code,
                'ip' => $this->getClientIp(),
            ];
            
            // Opcjonalne kolumny - dodaj tylko jeśli istnieją
            $optionalColumns = [
                'endpoint' => $endpoint,
                'method' => $method,
                'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? null,
                'playlists_count' => $playlistsCount,
                'response_code' => $responseCode,
                'response_status' => $this->getResponseStatus($responseCode),
                'response_time_ms' => $responseTimeMs,
            ];
            
            foreach ($optionalColumns as $col => $val) {
                if ($this->columnExists('api_logs', $col)) {
                    $columns[] = $col;
                    $values[] = ':' . $col;
                    $params[$col] = $val;
                }
            }
            
            $columns[] = 'created_at';
            $values[] = "datetime('now')";
            
            $sql = "INSERT INTO api_logs (" . implode(', ', $columns) . ") VALUES (" . implode(', ', $values) . ")";
            $this->db->insert($sql, $params);
            
        } catch (Exception $e) {
            $this->logToFile('api.log', "[API_LOG_ERROR] " . $e->getMessage());
        }
    }
    
    /**
     * Mapuj kod HTTP na status
     */
    private function getResponseStatus(int $code): string
    {
        if ($code >= 200 && $code < 300) {
            return 'success';
        } elseif ($code == 403) {
            return 'blocked';
        } elseif ($code == 404) {
            return 'not_found';
        } elseif ($code >= 400 && $code < 500) {
            return 'error';
        } else {
            return 'unknown';
        }
    }
    
    /**
     * Sprawdź czy kolumna istnieje w tabeli
     */
    private function columnExists(string $table, string $column): bool
    {
        static $cache = [];
        $key = "{$table}.{$column}";
        
        if (!isset($cache[$key])) {
            try {
                $result = $this->db->fetchAll("PRAGMA table_info({$table})");
                $columns = array_column($result, 'name');
                $cache[$key] = in_array($column, $columns);
            } catch (Exception $e) {
                $cache[$key] = false;
            }
        }
        
        return $cache[$key];
    }

    /**
     * Loguj do pliku
     */
    public function logToFile(string $filename, string $message): void
    {
        if (!defined('LOGS_PATH')) {
            return;
        }
        
        $logPath = LOGS_PATH . '/' . $filename;
        
        if (!is_dir(LOGS_PATH)) {
            @mkdir(LOGS_PATH, 0755, true);
        }
        
        $entry = sprintf(
            "[%s] [%s] %s\n",
            date('Y-m-d H:i:s'),
            $this->getClientIp(),
            $message
        );
        
        @file_put_contents($logPath, $entry, FILE_APPEND | LOCK_EX);
    }

    /**
     * Pobierz logi z bazy
     */
    public function getLogs(int $limit = 100, int $offset = 0, ?int $userId = null): array
    {
        $ipCol = $this->logsIpColumn ?? 'ip';
        $sql = "SELECT l.*, l.{$ipCol} as ip_address, u.username 
                FROM logs l 
                LEFT JOIN users u ON l.user_id = u.id";
        
        $params = [];
        
        if ($userId !== null) {
            $sql .= " WHERE l.user_id = :user_id";
            $params['user_id'] = $userId;
        }
        
        $sql .= " ORDER BY l.created_at DESC LIMIT :limit OFFSET :offset";
        $params['limit'] = $limit;
        $params['offset'] = $offset;
        
        return $this->db->fetchAll($sql, $params);
    }

    /**
     * Pobierz logi API
     */
    public function getApiLogs(int $limit = 100, int $offset = 0): array
    {
        $ipCol = $this->apiLogsIpColumn ?? 'ip';
        return $this->db->fetchAll(
            "SELECT al.*, al.{$ipCol} as ip, d.name as device_name, u.username 
             FROM api_logs al 
             LEFT JOIN devices d ON al.device_id = d.id 
             LEFT JOIN users u ON d.user_id = u.id
             ORDER BY al.created_at DESC 
             LIMIT :limit OFFSET :offset",
            ['limit' => $limit, 'offset' => $offset]
        );
    }

    /**
     * Zlicz logi
     */
    public function countLogs(?int $userId = null): int
    {
        if ($userId !== null) {
            $result = $this->db->fetchOne(
                "SELECT COUNT(*) as count FROM logs WHERE user_id = :user_id",
                ['user_id' => $userId]
            );
        } else {
            $result = $this->db->fetchOne("SELECT COUNT(*) as count FROM logs");
        }
        
        return $result['count'] ?? 0;
    }

    /**
     * Zlicz logi API
     */
    public function countApiLogs(): int
    {
        $result = $this->db->fetchOne("SELECT COUNT(*) as count FROM api_logs");
        return $result['count'] ?? 0;
    }

    /**
     * Wyczyść stare logi
     */
    public function clearOldLogs(int $daysToKeep = 30): int
    {
        $this->db->execute(
            "DELETE FROM logs WHERE created_at < datetime('now', :days)",
            ['days' => "-{$daysToKeep} days"]
        );
        $logsDeleted = $this->db->rowCount();
        
        $this->db->execute(
            "DELETE FROM api_logs WHERE created_at < datetime('now', :days)",
            ['days' => "-{$daysToKeep} days"]
        );
        $apiLogsDeleted = $this->db->rowCount();
        
        return $logsDeleted + $apiLogsDeleted;
    }

    /**
     * Pobierz IP klienta
     */
    private function getClientIp(): string
    {
        $headers = ['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'REMOTE_ADDR'];
        
        foreach ($headers as $header) {
            if (!empty($_SERVER[$header])) {
                $ip = $_SERVER[$header];
                if (strpos($ip, ',') !== false) {
                    $ip = trim(explode(',', $ip)[0]);
                }
                if (filter_var($ip, FILTER_VALIDATE_IP)) {
                    return $ip;
                }
            }
        }
        
        return '0.0.0.0';
    }
}
