<?php
declare(strict_types=1);

namespace Tests\Log;

use PHPUnit\Framework\TestCase;
use FreshCloud\Mail\Log\LogType;
use FreshCloud\Mail\Log\LogEntry;
use FreshCloud\Mail\Log\InMemoryLogger;
use FreshCloud\Mail\Log\Driver;
use FreshCloud\Mail\Log\PbAuditDriver;
use FreshCloud\Mail\PocketBaseAdapter\InMemoryPb;
use FreshCloud\Mail\Log\AuthBoundaryViolation;

final class LoggerTest extends TestCase
{
    private InMemoryPb $pb;
    private InMemoryLogger $logger;

    protected function setUp(): void
    {
        $this->pb = new InMemoryPb();
        $this->logger = new InMemoryLogger(pb: $this->pb, drivers: [new PbAuditDriver($this->pb)]);
    }

    public function testWriteCriticalPersistsImmediately(): void
    {
        $this->logger->write(LogType::Critical, 'critical message', ['user_id' => 'user1']);
        
        $audit = $this->pb->collection('email_audit')->getList(1, 10);
        $this->assertCount(1, $audit);
        $this->assertEquals('critical message', $audit[0]['message']);
    }

    public function testWriteDebugGoesToBufferWhenBelowThreshold(): void
    {
        $this->logger->write(LogType::Debug, 'debug message', ['user_id' => 'user1']);
        
        $audit = $this->pb->collection('email_audit')->getList(1, 10);
        $this->assertCount(0, $audit);
        
        $activity = $this->logger->getRecentActivity('user1', 50);
        $this->assertCount(1, $activity);
        $this->assertEquals('debug message', $activity[0]->message);
    }

    public function testAuditRequiresUserId(): void
    {
        $this->expectException(AuthBoundaryViolation::class);
        $this->logger->audit('test_action', ['account_id' => 'acc1']);
    }

    public function testAuditWithUserIdWritesToPb(): void
    {
        $this->logger->audit('test_action', ['user_id' => 'user1', 'extra' => 'data']);
        
        $audit = $this->pb->collection('email_audit')->getList(1, 10);
        $this->assertCount(1, $audit);
        $this->assertEquals('Audit action: test_action', $audit[0]['message']);
        $this->assertEquals('test_action', $audit[0]['action']);
    }

    public function testPbOfflineBufferReconnectSync(): void
    {
        // Simulate PB failure
        $this->pb->failNext(1);
        
        // Write some logs
        $this->logger->write(LogType::Debug, 'message1', ['user_id' => 'user1']);
        $this->logger->write(LogType::Error, 'message2', ['user_id' => 'user1']);
        
        // Verify buffer has entries
        $this->assertCount(2, $this->logger->getRecentActivity('user1', 50));
        
        // Flush and verify sync
        $this->logger->flush();
        
        $audit = $this->pb->collection('email_audit')->getList(1, 10);
        $this->assertCount(2, $audit);
        $this->assertEquals('message1', $audit[0]['message']);
        $this->assertEquals('message2', $audit[1]['message']);
    }

    public function testGetRecentActivityReturnsUsersEntries(): void
    {
        $this->logger->write(LogType::Info, 'user1 message', ['user_id' => 'user1']);
        $this->logger->write(LogType::Info, 'user2 message', ['user_id' => 'user2']);
        
        $activity = $this->logger->getRecentActivity('user1', 50);
        $this->assertCount(1, $activity);
        $this->assertEquals('user1 message', $activity[0]->message);
    }

    public function testInvalidLogTypeThrows(): void
    {
        $this->expectException(\ValueError::class);
        LogType::fromName('invalid');
    }

    public function testMessageTruncatedAt8192Chars(): void
    {
        $longMessage = str_repeat('a', 9000);
        $this->logger->write(LogType::Info, $longMessage, ['user_id' => 'user1']);

        $activity = $this->logger->getRecentActivity('user1', 50);
        $this->assertEquals(8192, strlen($activity[0]->message));
    }

    public function testBackwardCompatLogMethod(): void
    {
        $this->logger->log('compose.started', ['user_id' => 'user1']);
        
        $activity = $this->logger->getRecentActivity('user1', 50);
        $this->assertCount(1, $activity);
        $this->assertEquals('compose.started', $activity[0]->message);
        $this->assertEquals(LogType::Info, $activity[0]->level);
    }

    public function testMetricsEmitted(): void
    {
        $this->logger->write(LogType::Info, 'info message', ['user_id' => 'user1']);
        $this->logger->write(LogType::Error, 'error message', ['user_id' => 'user1']);
        
        $metrics = $this->logger->getMetrics();
        $this->assertEquals(1, $metrics['freshcloud_mail_log_writes_total']['levels']['info']);
        $this->assertEquals(1, $metrics['freshcloud_mail_log_writes_total']['levels']['error']);
    }
}