<?php

namespace Tests\Unit\Services\Log;

use App\Enums\LogLevelEnum;
use App\Enums\PlatformEnum;
use App\Models\Log;
use App\Models\PlatformChannel;
use App\Models\PlatformInstance;
use App\Services\Log\LogSaver;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class LogSaverTest extends TestCase
{
    use RefreshDatabase;

    private LogSaver $logSaver;

    protected function setUp(): void
    {
        parent::setUp();
        $this->logSaver = new LogSaver;
    }

    public function test_info_creates_log_record_with_info_level(): void
    {
        $message = 'Test info message';
        $context = ['key' => 'value'];

        $this->logSaver->info($message, null, $context);

        $this->assertDatabaseHas('logs', [
            'level' => LogLevelEnum::INFO,
            'message' => $message,
        ]);

        $log = Log::first();
        $this->assertEquals($context, $log->context);
    }

    public function test_error_creates_log_record_with_error_level(): void
    {
        $message = 'Test error message';
        $context = ['error_code' => 500];

        $this->logSaver->error($message, null, $context);

        $this->assertDatabaseHas('logs', [
            'level' => LogLevelEnum::ERROR,
            'message' => $message,
        ]);

        $log = Log::first();
        $this->assertEquals($context, $log->context);
    }

    public function test_warning_creates_log_record_with_warning_level(): void
    {
        $message = 'Test warning message';
        $context = ['warning_type' => 'deprecation'];

        $this->logSaver->warning($message, null, $context);

        $this->assertDatabaseHas('logs', [
            'level' => LogLevelEnum::WARNING,
            'message' => $message,
        ]);

        $log = Log::first();
        $this->assertEquals($context, $log->context);
    }

    public function test_debug_creates_log_record_with_debug_level(): void
    {
        $message = 'Test debug message';
        $context = ['debug_info' => 'trace'];

        $this->logSaver->debug($message, null, $context);

        $this->assertDatabaseHas('logs', [
            'level' => LogLevelEnum::DEBUG,
            'message' => $message,
        ]);

        $log = Log::first();
        $this->assertEquals($context, $log->context);
    }

    public function test_log_with_channel_includes_channel_information_in_context(): void
    {
        $platformInstance = PlatformInstance::factory()->create([
            'platform' => PlatformEnum::LEMMY,
            'url' => 'https://lemmy.example.com',
        ]);

        $channel = PlatformChannel::factory()->create([
            'name' => 'Test Channel',
            'platform_instance_id' => $platformInstance->id,
        ]);

        $message = 'Test message with channel';
        $originalContext = ['original_key' => 'original_value'];

        $this->logSaver->info($message, $channel, $originalContext);

        $log = Log::first();

        $expectedContext = array_merge($originalContext, [
            'channel_id' => $channel->id,
            'channel_name' => 'Test Channel',
            'platform' => PlatformEnum::LEMMY->value,
            'instance_url' => 'https://lemmy.example.com',
        ]);

        $this->assertEquals($expectedContext, $log->context);
        $this->assertEquals($message, $log->message);
        $this->assertEquals(LogLevelEnum::INFO, $log->level);
    }

    public function test_log_without_channel_uses_original_context_only(): void
    {
        $message = 'Test message without channel';
        $context = ['test_key' => 'test_value'];

        $this->logSaver->info($message, null, $context);

        $log = Log::first();

        $this->assertEquals($context, $log->context);
        $this->assertEquals($message, $log->message);
    }

    public function test_log_with_empty_context_creates_minimal_log(): void
    {
        $message = 'Simple message';

        $this->logSaver->info($message);

        $this->assertDatabaseHas('logs', [
            'level' => LogLevelEnum::INFO,
            'message' => $message,
        ]);

        $log = Log::first();
        $this->assertEquals([], $log->context);
    }

    public function test_log_with_channel_but_empty_context_includes_only_channel_info(): void
    {
        $platformInstance = PlatformInstance::factory()->create([
            'platform' => PlatformEnum::LEMMY,
            'url' => 'https://test.lemmy.com',
        ]);

        $channel = PlatformChannel::factory()->create([
            'name' => 'Empty Context Channel',
            'platform_instance_id' => $platformInstance->id,
        ]);

        $message = 'Message with channel but no context';

        $this->logSaver->warning($message, $channel);

        $log = Log::first();

        $expectedContext = [
            'channel_id' => $channel->id,
            'channel_name' => 'Empty Context Channel',
            'platform' => PlatformEnum::LEMMY->value,
            'instance_url' => 'https://test.lemmy.com',
        ];

        $this->assertEquals($expectedContext, $log->context);
        $this->assertEquals(LogLevelEnum::WARNING, $log->level);
    }

    public function test_context_merging_preserves_original_keys_and_adds_channel_info(): void
    {
        $platformInstance = PlatformInstance::factory()->create([
            'platform' => PlatformEnum::LEMMY,
            'url' => 'https://merge.lemmy.com',
        ]);

        $channel = PlatformChannel::factory()->create([
            'name' => 'Merge Test Channel',
            'platform_instance_id' => $platformInstance->id,
        ]);

        $originalContext = [
            'article_id' => 123,
            'user_action' => 'publish',
            'timestamp' => '2024-01-01 12:00:00',
        ];

        $this->logSaver->error('Context merge test', $channel, $originalContext);

        $log = Log::first();

        $expectedContext = [
            'article_id' => 123,
            'user_action' => 'publish',
            'timestamp' => '2024-01-01 12:00:00',
            'channel_id' => $channel->id,
            'channel_name' => 'Merge Test Channel',
            'platform' => PlatformEnum::LEMMY->value,
            'instance_url' => 'https://merge.lemmy.com',
        ];

        $this->assertEquals($expectedContext, $log->context);
    }

    public function test_multiple_logs_are_created_independently(): void
    {
        $this->logSaver->info('First message', null, ['id' => 1]);
        $this->logSaver->error('Second message', null, ['id' => 2]);
        $this->logSaver->warning('Third message', null, ['id' => 3]);

        $this->assertDatabaseCount('logs', 3);

        $logs = Log::orderBy('id')->get();

        $this->assertEquals(LogLevelEnum::INFO, $logs[0]->level);
        $this->assertEquals('First message', $logs[0]->message);
        $this->assertEquals(['id' => 1], $logs[0]->context);

        $this->assertEquals(LogLevelEnum::ERROR, $logs[1]->level);
        $this->assertEquals('Second message', $logs[1]->message);
        $this->assertEquals(['id' => 2], $logs[1]->context);

        $this->assertEquals(LogLevelEnum::WARNING, $logs[2]->level);
        $this->assertEquals('Third message', $logs[2]->message);
        $this->assertEquals(['id' => 3], $logs[2]->context);
    }

    public function test_log_with_complex_context_data(): void
    {
        $complexContext = [
            'nested' => [
                'array' => ['value1', 'value2'],
                'object' => ['key' => 'value'],
            ],
            'numbers' => [1, 2, 3.14],
            'boolean' => true,
            'null_value' => null,
        ];

        $this->logSaver->debug('Complex context test', null, $complexContext);

        $log = Log::first();
        $this->assertEquals($complexContext, $log->context);
    }

    public function test_each_log_level_method_delegates_to_private_log_method(): void
    {
        $message = 'Test message';
        $context = ['test' => true];

        // Test all four log level methods
        $this->logSaver->info($message, null, $context);
        $this->logSaver->error($message, null, $context);
        $this->logSaver->warning($message, null, $context);
        $this->logSaver->debug($message, null, $context);

        // Should have 4 log entries
        $this->assertDatabaseCount('logs', 4);

        $logs = Log::orderBy('id')->get();

        // Verify each level was set correctly
        $this->assertEquals(LogLevelEnum::INFO, $logs[0]->level);
        $this->assertEquals(LogLevelEnum::ERROR, $logs[1]->level);
        $this->assertEquals(LogLevelEnum::WARNING, $logs[2]->level);
        $this->assertEquals(LogLevelEnum::DEBUG, $logs[3]->level);

        // All should have the same message and context
        foreach ($logs as $log) {
            $this->assertEquals($message, $log->message);
            $this->assertEquals($context, $log->context);
        }
    }
}
