<?php

declare(strict_types=1);

namespace Tests\Feature\Framework\Logging;

use App\Framework\Core\PathProvider;
use App\Framework\Logging\DefaultLogger;
use App\Framework\Logging\Handlers\MultiFileHandler;
use App\Framework\Logging\LogConfig;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\ProcessorManager;
use PHPUnit\Framework\TestCase;

final class ChannelLoggingIntegrationTest extends TestCase
{
    private string $testLogDir;

    private DefaultLogger $logger;

    private LogConfig $logConfig;

    protected function setUp(): void
    {
        // Temporäres Test-Verzeichnis erstellen
        $this->testLogDir = sys_get_temp_dir() . '/integration_logs_' . uniqid();
        mkdir($this->testLogDir, 0755, true);

        // PathProvider Mock
        $pathProvider = $this->createMock(PathProvider::class);
        $pathProvider->method('resolvePath')->willReturnCallback(function (string $path) {
            return $this->testLogDir . '/' . $path;
        });

        // LogConfig Mock
        $this->logConfig = $this->createMock(LogConfig::class);
        $this->logConfig->method('getLogPath')->willReturnCallback(function (string $type) {
            return match ($type) {
                'security' => $this->testLogDir . '/security/security.log',
                'cache' => $this->testLogDir . '/debug/cache.log',
                'database' => $this->testLogDir . '/debug/database.log',
                'framework' => $this->testLogDir . '/debug/framework.log',
                'error' => $this->testLogDir . '/app/error.log',
                default => $this->testLogDir . '/app/app.log'
            };
        });

        // MultiFileHandler
        $multiFileHandler = new MultiFileHandler(
            $this->logConfig,
            $pathProvider,
            LogLevel::DEBUG,
            '[{timestamp}] [{level_name}] [{channel}] {message}'
        );

        // DefaultLogger mit echtem MultiFileHandler
        $this->logger = new DefaultLogger(
            minLevel: LogLevel::DEBUG,
            handlers: [$multiFileHandler],
            processorManager: new ProcessorManager()
        );
    }

    protected function tearDown(): void
    {
        // Cleanup: Test-Verzeichnis löschen
        if (is_dir($this->testLogDir)) {
            $this->deleteDirectory($this->testLogDir);
        }
    }

    private function deleteDirectory(string $dir): void
    {
        if (! is_dir($dir)) {
            return;
        }

        $files = array_diff(scandir($dir), ['.', '..']);
        foreach ($files as $file) {
            $path = $dir . '/' . $file;
            is_dir($path) ? $this->deleteDirectory($path) : unlink($path);
        }
        rmdir($dir);
    }

    public function test_complete_channel_system_integration(): void
    {
        // Verschiedene Channel-Logs erstellen
        $this->logger->security->warning('Failed login attempt', ['email' => 'test@example.com', 'ip' => '192.168.1.1']);
        $this->logger->cache->debug('Cache miss', ['key' => 'user_123']);
        $this->logger->database->error('Query timeout', ['query' => 'SELECT * FROM users', 'duration' => 30.5]);
        $this->logger->framework->info('Route registered', ['route' => '/api/users', 'method' => 'GET']);
        $this->logger->error->critical('System failure', ['component' => 'payment', 'error_code' => 'PAY_001']);

        // Alle Log-Dateien sollten erstellt worden sein
        expect(file_exists($this->testLogDir . '/security/security.log'))->toBeTrue();
        expect(file_exists($this->testLogDir . '/debug/cache.log'))->toBeTrue();
        expect(file_exists($this->testLogDir . '/debug/database.log'))->toBeTrue();
        expect(file_exists($this->testLogDir . '/debug/framework.log'))->toBeTrue();
        expect(file_exists($this->testLogDir . '/app/error.log'))->toBeTrue();

        // Security Log Inhalt prüfen
        $securityContent = file_get_contents($this->testLogDir . '/security/security.log');
        expect($securityContent)->toContain('[WARNING] [security] Failed login attempt');
        expect($securityContent)->toContain('"email":"test@example.com"');
        expect($securityContent)->toContain('"ip":"192.168.1.1"');

        // Cache Log Inhalt prüfen
        $cacheContent = file_get_contents($this->testLogDir . '/debug/cache.log');
        expect($cacheContent)->toContain('[DEBUG] [cache] Cache miss');
        expect($cacheContent)->toContain('"key":"user_123"');

        // Database Log Inhalt prüfen
        $databaseContent = file_get_contents($this->testLogDir . '/debug/database.log');
        expect($databaseContent)->toContain('[ERROR] [database] Query timeout');
        expect($databaseContent)->toContain('"query":"SELECT * FROM users"');
        expect($databaseContent)->toContain('"duration":30.5');

        // Framework Log Inhalt prüfen
        $frameworkContent = file_get_contents($this->testLogDir . '/debug/framework.log');
        expect($frameworkContent)->toContain('[INFO] [framework] Route registered');
        expect($frameworkContent)->toContain('"route":"\/api\/users"');
        expect($frameworkContent)->toContain('"method":"GET"');

        // Error Log Inhalt prüfen
        $errorContent = file_get_contents($this->testLogDir . '/app/error.log');
        expect($errorContent)->toContain('[CRITICAL] [error] System failure');
        expect($errorContent)->toContain('"component":"payment"');
        expect($errorContent)->toContain('"error_code":"PAY_001"');
    }

    public function test_mixed_standard_and_channel_logging(): void
    {
        // Mix aus Standard-Logging und Channel-Logging
        $this->logger->info('Standard application log');
        $this->logger->security->error('Security incident');
        $this->logger->warning('Another standard log');
        $this->logger->cache->info('Cache operation');

        // Standard-Logs sollten nicht in den spezifischen Channel-Dateien landen
        expect(file_exists($this->testLogDir . '/security/security.log'))->toBeTrue();
        expect(file_exists($this->testLogDir . '/debug/cache.log'))->toBeTrue();

        // Security Log sollte nur Security-spezifische Einträge haben
        $securityContent = file_get_contents($this->testLogDir . '/security/security.log');
        expect($securityContent)->toContain('Security incident');
        expect($securityContent)->not->toContain('Standard application log');
        expect($securityContent)->not->toContain('Another standard log');

        // Cache Log sollte nur Cache-spezifische Einträge haben
        $cacheContent = file_get_contents($this->testLogDir . '/debug/cache.log');
        expect($cacheContent)->toContain('Cache operation');
        expect($cacheContent)->not->toContain('Standard application log');
        expect($cacheContent)->not->toContain('Security incident');
    }

    public function test_multiple_entries_same_channel(): void
    {
        // Mehrere Einträge in den gleichen Channel
        $this->logger->security->info('User login', ['user_id' => 123]);
        $this->logger->security->warning('Suspicious activity', ['user_id' => 123, 'actions' => ['login', 'password_change']]);
        $this->logger->security->error('Account locked', ['user_id' => 123, 'reason' => 'too_many_attempts']);

        $securityFile = $this->testLogDir . '/security/security.log';
        expect(file_exists($securityFile))->toBeTrue();

        $content = file_get_contents($securityFile);
        $lines = explode("\n", trim($content));

        // Drei Zeilen sollten vorhanden sein
        expect($lines)->toHaveCount(3);

        expect($content)->toContain('[INFO] [security] User login');
        expect($content)->toContain('[WARNING] [security] Suspicious activity');
        expect($content)->toContain('[ERROR] [security] Account locked');

        // Alle Einträge sollten user_id 123 enthalten
        expect($content)->toContain('"user_id":123');
    }

    public function test_channel_system_with_different_log_levels(): void
    {
        // Verschiedene Log-Level in verschiedenen Channels
        $this->logger->cache->debug('Debug cache operation');
        $this->logger->security->info('Info security event');
        $this->logger->database->warning('Warning database issue');
        $this->logger->framework->error('Error in framework');
        $this->logger->error->critical('Critical system error');

        // Alle Dateien sollten existieren
        $files = [
            'cache' => $this->testLogDir . '/debug/cache.log',
            'security' => $this->testLogDir . '/security/security.log',
            'database' => $this->testLogDir . '/debug/database.log',
            'framework' => $this->testLogDir . '/debug/framework.log',
            'error' => $this->testLogDir . '/app/error.log',
        ];

        foreach ($files as $channel => $file) {
            expect(file_exists($file))->toBeTrue("File for {$channel} channel should exist");
        }

        // Level sollten korrekt sein
        expect(file_get_contents($files['cache']))->toContain('[DEBUG]');
        expect(file_get_contents($files['security']))->toContain('[INFO]');
        expect(file_get_contents($files['database']))->toContain('[WARNING]');
        expect(file_get_contents($files['framework']))->toContain('[ERROR]');
        expect(file_get_contents($files['error']))->toContain('[CRITICAL]');
    }

    public function test_realistic_application_scenario(): void
    {
        // Simuliere eine realistische Anwendungsszenario

        // User Authentication Flow
        $this->logger->security->info('Authentication attempt started', ['email' => 'user@example.com']);
        $this->logger->cache->debug('Checking user cache', ['cache_key' => 'user_user@example.com']);
        $this->logger->cache->debug('Cache miss - loading from database');
        $this->logger->database->debug('User query executed', ['query' => 'SELECT * FROM users WHERE email = ?']);
        $this->logger->security->info('User authenticated successfully', ['user_id' => 42]);
        $this->logger->cache->info('User cached', ['cache_key' => 'user_42', 'ttl' => 3600]);

        // API Request Processing
        $this->logger->framework->debug('Route matched', ['route' => '/api/orders', 'method' => 'GET']);
        $this->logger->database->debug('Orders query', ['user_id' => 42, 'limit' => 10]);
        $this->logger->cache->debug('Cache hit', ['cache_key' => 'orders_42_page_1']);

        // Error Scenario
        $this->logger->database->error('Database connection lost', ['host' => 'db.example.com', 'port' => 3306]);
        $this->logger->error->critical('Service degraded', ['affected_services' => ['orders', 'payments']]);
        $this->logger->security->warning('Potential attack detected', ['ip' => '1.2.3.4', 'pattern' => 'sql_injection']);

        // Überprüfe dass alle Logs korrekt in ihre Kanäle geschrieben wurden
        $securityLog = file_get_contents($this->testLogDir . '/security/security.log');
        $cacheLog = file_get_contents($this->testLogDir . '/debug/cache.log');
        $databaseLog = file_get_contents($this->testLogDir . '/debug/database.log');
        $frameworkLog = file_get_contents($this->testLogDir . '/debug/framework.log');
        $errorLog = file_get_contents($this->testLogDir . '/app/error.log');

        // Security-Ereignisse
        expect($securityLog)->toContain('Authentication attempt started');
        expect($securityLog)->toContain('User authenticated successfully');
        expect($securityLog)->toContain('Potential attack detected');

        // Cache-Operationen
        expect($cacheLog)->toContain('Checking user cache');
        expect($cacheLog)->toContain('Cache miss');
        expect($cacheLog)->toContain('User cached');
        expect($cacheLog)->toContain('Cache hit');

        // Database-Operationen
        expect($databaseLog)->toContain('User query executed');
        expect($databaseLog)->toContain('Orders query');
        expect($databaseLog)->toContain('Database connection lost');

        // Framework-Ereignisse
        expect($frameworkLog)->toContain('Route matched');

        // Error-Ereignisse
        expect($errorLog)->toContain('Service degraded');

        // Cross-channel Isolation prüfen
        expect($securityLog)->not->toContain('Cache miss');
        expect($cacheLog)->not->toContain('Authentication attempt');
        expect($databaseLog)->not->toContain('Route matched');
    }
}
