<?php

namespace Tests\Feature;

use App\Jobs\SyncTerminalConfigJob;
use App\Models\Terminal;
use App\Models\TerminalSyncLog;
use App\Models\KnowledgeBase;
use App\Models\TerminalPrompt;
use App\Services\TerminalSyncService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Queue;
use Tests\TestCase;

class TerminalSyncTest extends TestCase
{
    use RefreshDatabase;

    protected TerminalSyncService $syncService;

    protected function setUp(): void
    {
        parent::setUp();
        $this->syncService = app(TerminalSyncService::class);
    }

    /**
     * 测试单个终端同步
     */
    public function test_can_sync_single_terminal(): void
    {
        Queue::fake();

        // 创建测试终端
        $terminal = Terminal::factory()->create([
            'name' => '测试终端',
            'code' => 'TEST-001',
        ]);

        // 执行同步
        $log = $this->syncService->syncConfiguration($terminal);

        // 断言同步日志已创建
        $this->assertInstanceOf(TerminalSyncLog::class, $log);
        $this->assertEquals('pending', $log->status);
        $this->assertEquals($terminal->id, $log->terminal_id);
        $this->assertNotNull($log->config_snapshot);

        // 断言任务已加入队列
        Queue::assertPushed(SyncTerminalConfigJob::class, function ($job) use ($terminal, $log) {
            return $job->terminal->id === $terminal->id 
                && $job->log->id === $log->id;
        });
    }

    /**
     * 测试配置快照包含完整信息
     */
    public function test_config_snapshot_contains_complete_information(): void
    {
        // 创建终端及关联数据
        $terminal = Terminal::factory()->create([
            'name' => '测试终端',
            'code' => 'TEST-002',
            'ip_address' => '192.168.1.100',
            'station_id' => 1,
            'diagram_url' => 'https://example.com/diagram.png',
            'display_config' => ['resolution' => '1920x1080'],
        ]);

        // 创建知识库关联
        $kb1 = KnowledgeBase::factory()->create(['name' => '知识库1']);
        $kb2 = KnowledgeBase::factory()->create(['name' => '知识库2']);
        $terminal->knowledgeBases()->attach($kb1->id, ['priority' => 1]);
        $terminal->knowledgeBases()->attach($kb2->id, ['priority' => 2]);

        // 创建提示词
        TerminalPrompt::factory()->create([
            'terminal_id' => $terminal->id,
            'prompt_template' => '你好，{user}',
            'variables' => ['user' => 'string'],
        ]);

        // 获取配置快照
        $snapshot = $this->syncService->getConfigSnapshot($terminal);

        // 断言快照包含终端信息
        $this->assertArrayHasKey('terminal', $snapshot);
        $this->assertEquals('测试终端', $snapshot['terminal']['name']);
        $this->assertEquals('TEST-002', $snapshot['terminal']['code']);
        $this->assertEquals('192.168.1.100', $snapshot['terminal']['ip_address']);

        // 断言快照包含知识库信息
        $this->assertArrayHasKey('knowledge_bases', $snapshot);
        $this->assertCount(2, $snapshot['knowledge_bases']);
        $this->assertEquals('知识库1', $snapshot['knowledge_bases'][0]['name']);
        $this->assertEquals(1, $snapshot['knowledge_bases'][0]['priority']);

        // 断言快照包含提示词信息
        $this->assertArrayHasKey('prompt', $snapshot);
        $this->assertEquals('你好，{user}', $snapshot['prompt']['prompt_template']);
    }

    /**
     * 测试批量同步
     */
    public function test_can_batch_sync_terminals(): void
    {
        Queue::fake();

        // 创建多个终端
        $terminals = Terminal::factory()->count(3)->create();
        $terminalIds = $terminals->pluck('id')->toArray();

        // 执行批量同步
        $logs = $this->syncService->batchSync($terminalIds);

        // 断言创建了3个同步日志
        $this->assertCount(3, $logs);
        
        // 断言每个终端都有同步日志
        foreach ($terminals as $terminal) {
            $this->assertDatabaseHas('terminal_sync_logs', [
                'terminal_id' => $terminal->id,
                'status' => 'pending',
            ]);
        }

        // 断言任务已加入队列
        Queue::assertPushed(SyncTerminalConfigJob::class, 3);
    }

    /**
     * 测试同步状态更新
     */
    public function test_can_update_sync_status(): void
    {
        $terminal = Terminal::factory()->create();
        $log = TerminalSyncLog::create([
            'terminal_id' => $terminal->id,
            'status' => 'pending',
            'config_snapshot' => [],
        ]);

        // 更新为同步中
        $this->syncService->updateSyncStatus($log, 'syncing');
        $this->assertEquals('syncing', $log->fresh()->status);

        // 更新为已同步
        $this->syncService->updateSyncStatus($log, 'synced');
        $log->refresh();
        $this->assertEquals('synced', $log->status);
        $this->assertNotNull($log->synced_at);

        // 更新为失败
        $this->syncService->updateSyncStatus($log, 'failed', '测试错误');
        $log->refresh();
        $this->assertEquals('failed', $log->status);
        $this->assertEquals('测试错误', $log->error_message);
    }

    /**
     * 测试同步任务成功执行
     */
    public function test_sync_job_executes_successfully(): void
    {
        $terminal = Terminal::factory()->create();
        $log = TerminalSyncLog::create([
            'terminal_id' => $terminal->id,
            'status' => 'pending',
            'config_snapshot' => [],
        ]);

        // 执行任务
        $job = new SyncTerminalConfigJob($terminal, $log);
        $job->handle();

        // 断言状态更新为已同步
        $log->refresh();
        $this->assertEquals('synced', $log->status);
        $this->assertNotNull($log->synced_at);
        $this->assertNull($log->error_message);
    }

    /**
     * 测试同步失败处理
     */
    public function test_sync_job_handles_failure(): void
    {
        $terminal = Terminal::factory()->create();
        $log = TerminalSyncLog::create([
            'terminal_id' => $terminal->id,
            'status' => 'pending',
            'config_snapshot' => [],
        ]);

        $job = new SyncTerminalConfigJob($terminal, $log);

        // 模拟失败（通过调用failed方法）
        $exception = new \Exception('测试同步失败');
        $job->failed($exception);

        // 断言状态更新为失败
        $log->refresh();
        $this->assertEquals('failed', $log->status);
        $this->assertStringContainsString('测试同步失败', $log->error_message);
    }

    /**
     * 测试同步历史记录
     */
    public function test_can_view_sync_history(): void
    {
        $terminal = Terminal::factory()->create();

        // 创建多条同步记录
        TerminalSyncLog::create([
            'terminal_id' => $terminal->id,
            'status' => 'synced',
            'config_snapshot' => [],
            'synced_at' => now()->subHours(2),
            'created_at' => now()->subHours(2),
        ]);

        TerminalSyncLog::create([
            'terminal_id' => $terminal->id,
            'status' => 'failed',
            'config_snapshot' => [],
            'error_message' => '网络错误',
            'created_at' => now()->subHour(),
        ]);

        TerminalSyncLog::create([
            'terminal_id' => $terminal->id,
            'status' => 'synced',
            'config_snapshot' => [],
            'synced_at' => now(),
            'created_at' => now(),
        ]);

        // 获取同步历史（应该按时间倒序）
        $logs = $terminal->syncLogs;

        $this->assertCount(3, $logs);
        // 最新的记录应该在第一位
        $this->assertEquals('synced', $logs[0]->status);
        $this->assertEquals('failed', $logs[1]->status);
        $this->assertEquals('synced', $logs[2]->status);
    }

    /**
     * 测试获取最新同步日志
     */
    public function test_can_get_latest_sync_log(): void
    {
        $terminal = Terminal::factory()->create();

        // 创建多条同步记录
        TerminalSyncLog::create([
            'terminal_id' => $terminal->id,
            'status' => 'synced',
            'config_snapshot' => [],
            'created_at' => now()->subHours(2),
        ]);

        $latestLog = TerminalSyncLog::create([
            'terminal_id' => $terminal->id,
            'status' => 'pending',
            'config_snapshot' => [],
            'created_at' => now(),
        ]);

        // 获取最新同步日志
        $result = $terminal->latestSyncLog;

        $this->assertNotNull($result);
        $this->assertEquals($latestLog->id, $result->id);
        $this->assertEquals('pending', $result->status);
    }
}
