<?php

namespace Tests\Feature\Modules;

use Tests\TestCase;
use App\Modules\AnalyticsModule;
use App\Services\Core\ModuleManager;
use App\Services\Core\EventBus;
use App\Services\Core\ConfigurationManager;
use App\DTOs\TelegramUpdateDTO;
use App\Models\User;
use App\Models\AnalyticsEvent;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery;

class AnalyticsModuleTest extends TestCase
{
    use RefreshDatabase;

    private AnalyticsModule $module;
    private $mockModuleManager;
    private $mockEventBus;
    private $mockConfigManager;

    protected function setUp(): void
    {
        parent::setUp();
        
        $this->mockModuleManager = Mockery::mock(ModuleManager::class);
        $this->mockEventBus = Mockery::mock(EventBus::class);
        $this->mockConfigManager = Mockery::mock(ConfigurationManager::class);
        
        $this->module = new AnalyticsModule(
            $this->mockModuleManager,
            $this->mockEventBus,
            $this->mockConfigManager
        );
    }

    protected function tearDown(): void
    {
        Mockery::close();
        parent::tearDown();
    }

    public function test_module_registration()
    {
        $this->assertEquals('AnalyticsModule', $this->module->getName());
        $this->assertEquals('1.0.0', $this->module->getVersion());
        $this->assertNotEmpty($this->module->getDescription());
        $this->assertContains('/analytics', $this->module->getHandledCommands());
        $this->assertContains('/stats', $this->module->getHandledCommands());
    }

    public function test_handles_analytics_command()
    {
        $user = User::factory()->create(['telegram_id' => 67890, 'is_admin' => true]);
        
        $updateData = [
            'message' => [
                'text' => '/analytics',
                'chat' => ['id' => 12345],
                'from' => ['id' => 67890, 'first_name' => 'Admin']
            ]
        ];

        $update = TelegramUpdateDTO::fromArray($updateData);

        $this->mockEventBus->shouldReceive('dispatch')
            ->with('telegram.send_message', Mockery::any())
            ->once();

        $result = $this->module->handleUpdate($update);
        $this->assertTrue($result);
    }

    public function test_handles_stats_command()
    {
        $user = User::factory()->create(['telegram_id' => 67890]);
        
        // Create some analytics events for the user
        AnalyticsEvent::factory()->count(5)->create(['user_id' => $user->id]);
        
        $updateData = [
            'message' => [
                'text' => '/stats',
                'chat' => ['id' => 12345],
                'from' => ['id' => 67890, 'first_name' => 'Test']
            ]
        ];

        $update = TelegramUpdateDTO::fromArray($updateData);

        $this->mockEventBus->shouldReceive('dispatch')
            ->with('telegram.send_message', Mockery::any())
            ->once();

        $result = $this->module->handleUpdate($update);
        $this->assertTrue($result);
    }

    public function test_tracks_command_usage()
    {
        $user = User::factory()->create(['telegram_id' => 67890]);
        
        $this->module->trackEvent($user->id, 'command_used', [
            'command' => '/help',
            'timestamp' => now()
        ]);

        $this->assertDatabaseHas('analytics_events', [
            'user_id' => $user->id,
            'event_type' => 'command_used'
        ]);
    }

    public function test_tracks_user_interactions()
    {
        $user = User::factory()->create(['telegram_id' => 67890]);
        
        $this->module->trackEvent($user->id, 'button_clicked', [
            'button_data' => 'payment_10000',
            'timestamp' => now()
        ]);

        $this->assertDatabaseHas('analytics_events', [
            'user_id' => $user->id,
            'event_type' => 'button_clicked'
        ]);
    }

    public function test_handles_analytics_period_callback()
    {
        $user = User::factory()->create(['telegram_id' => 67890, 'is_admin' => true]);
        
        $updateData = [
            'callback_query' => [
                'data' => 'analytics_today',
                'message' => [
                    'chat' => ['id' => 12345],
                    'message_id' => 123
                ],
                'from' => ['id' => 67890, 'first_name' => 'Admin']
            ]
        ];

        $update = TelegramUpdateDTO::fromArray($updateData);

        $this->mockEventBus->shouldReceive('dispatch')
            ->with('telegram.edit_message', Mockery::any())
            ->once();

        $result = $this->module->handleCallback($update);
        $this->assertTrue($result);
    }

    public function test_generates_user_statistics()
    {
        $user = User::factory()->create(['telegram_id' => 67890]);
        
        // Create various events
        AnalyticsEvent::factory()->create([
            'user_id' => $user->id,
            'event_type' => 'command_used',
            'created_at' => now()
        ]);
        
        AnalyticsEvent::factory()->create([
            'user_id' => $user->id,
            'event_type' => 'button_clicked',
            'created_at' => now()
        ]);

        $stats = $this->module->getUserStatistics($user->id);
        
        $this->assertIsArray($stats);
        $this->assertArrayHasKey('total_events', $stats);
        $this->assertArrayHasKey('commands_used', $stats);
        $this->assertArrayHasKey('buttons_clicked', $stats);
        $this->assertEquals(2, $stats['total_events']);
    }

    public function test_generates_system_analytics()
    {
        // Create users and events
        $users = User::factory()->count(3)->create();
        
        foreach ($users as $user) {
            AnalyticsEvent::factory()->count(2)->create([
                'user_id' => $user->id,
                'event_type' => 'command_used'
            ]);
        }

        $analytics = $this->module->getSystemAnalytics();
        
        $this->assertIsArray($analytics);
        $this->assertArrayHasKey('total_users', $analytics);
        $this->assertArrayHasKey('total_events', $analytics);
        $this->assertArrayHasKey('active_users_today', $analytics);
        $this->assertEquals(3, $analytics['total_users']);
        $this->assertEquals(6, $analytics['total_events']);
    }

    public function test_handles_export_analytics_callback()
    {
        $user = User::factory()->create(['telegram_id' => 67890, 'is_admin' => true]);
        
        $updateData = [
            'callback_query' => [
                'data' => 'analytics_export',
                'message' => [
                    'chat' => ['id' => 12345],
                    'message_id' => 123
                ],
                'from' => ['id' => 67890, 'first_name' => 'Admin']
            ]
        ];

        $update = TelegramUpdateDTO::fromArray($updateData);

        $this->mockEventBus->shouldReceive('dispatch')
            ->with('telegram.send_document', Mockery::any())
            ->once();

        $result = $this->module->handleCallback($update);
        $this->assertTrue($result);
    }

    public function test_tracks_session_duration()
    {
        $user = User::factory()->create(['telegram_id' => 67890]);
        
        $this->module->startSession($user->id);
        
        // Simulate some delay
        sleep(1);
        
        $this->module->endSession($user->id);

        $this->assertDatabaseHas('analytics_events', [
            'user_id' => $user->id,
            'event_type' => 'session_ended'
        ]);
    }

    public function test_calculates_user_retention()
    {
        $user = User::factory()->create([
            'telegram_id' => 67890,
            'created_at' => now()->subDays(7)
        ]);
        
        // Create events for different days
        AnalyticsEvent::factory()->create([
            'user_id' => $user->id,
            'event_type' => 'command_used',
            'created_at' => now()->subDays(6)
        ]);
        
        AnalyticsEvent::factory()->create([
            'user_id' => $user->id,
            'event_type' => 'command_used',
            'created_at' => now()->subDays(3)
        ]);
        
        AnalyticsEvent::factory()->create([
            'user_id' => $user->id,
            'event_type' => 'command_used',
            'created_at' => now()
        ]);

        $retention = $this->module->calculateUserRetention(7);
        
        $this->assertIsArray($retention);
        $this->assertArrayHasKey('day_1', $retention);
        $this->assertArrayHasKey('day_7', $retention);
    }

    public function test_tracks_popular_commands()
    {
        $users = User::factory()->count(3)->create();
        
        foreach ($users as $user) {
            $this->module->trackEvent($user->id, 'command_used', [
                'command' => '/help'
            ]);
        }
        
        $this->module->trackEvent($users[0]->id, 'command_used', [
            'command' => '/start'
        ]);

        $popularCommands = $this->module->getPopularCommands();
        
        $this->assertIsArray($popularCommands);
        $this->assertEquals('/help', $popularCommands[0]['command']);
        $this->assertEquals(3, $popularCommands[0]['count']);
    }

    public function test_handles_non_admin_analytics_access()
    {
        $user = User::factory()->create(['telegram_id' => 67890, 'is_admin' => false]);
        
        $updateData = [
            'message' => [
                'text' => '/analytics',
                'chat' => ['id' => 12345],
                'from' => ['id' => 67890, 'first_name' => 'User']
            ]
        ];

        $update = TelegramUpdateDTO::fromArray($updateData);

        $this->mockEventBus->shouldReceive('dispatch')
            ->with('telegram.send_message', Mockery::any())
            ->once();

        $result = $this->module->handleUpdate($update);
        $this->assertTrue($result); // Should send "access denied" message
    }

    public function test_module_initialization()
    {
        $this->mockEventBus->shouldReceive('listen')
            ->with('user.action', Mockery::any())
            ->once();

        $this->mockEventBus->shouldReceive('listen')
            ->with('command.executed', Mockery::any())
            ->once();

        $this->mockEventBus->shouldReceive('listen')
            ->with('callback.executed', Mockery::any())
            ->once();

        $result = $this->module->initialize();
        $this->assertTrue($result);
    }

    public function test_event_data_validation()
    {
        $user = User::factory()->create(['telegram_id' => 67890]);
        
        // Valid event data
        $result1 = $this->module->trackEvent($user->id, 'test_event', [
            'valid_key' => 'valid_value'
        ]);
        $this->assertTrue($result1);
        
        // Invalid event data (should handle gracefully)
        $result2 = $this->module->trackEvent($user->id, '', null);
        $this->assertFalse($result2);
    }

    public function test_analytics_data_aggregation()
    {
        $user = User::factory()->create(['telegram_id' => 67890]);
        
        // Create events across different time periods
        AnalyticsEvent::factory()->create([
            'user_id' => $user->id,
            'event_type' => 'command_used',
            'created_at' => now()->startOfDay()
        ]);
        
        AnalyticsEvent::factory()->create([
            'user_id' => $user->id,
            'event_type' => 'command_used',
            'created_at' => now()->subDay()->startOfDay()
        ]);

        $todayData = $this->module->getAnalyticsForPeriod('today');
        $yesterdayData = $this->module->getAnalyticsForPeriod('yesterday');
        
        $this->assertIsArray($todayData);
        $this->assertIsArray($yesterdayData);
        $this->assertArrayHasKey('events_count', $todayData);
        $this->assertArrayHasKey('events_count', $yesterdayData);
    }
}