<?php

declare(strict_types=1);

namespace Tests\Framework\Telemetry;

use App\Framework\CircuitBreaker\CircuitBreaker;
use App\Framework\DateTime\SystemClock;
use App\Framework\Logging\DefaultLogger;
use App\Framework\Performance\EnhancedPerformanceCollector;
use App\Framework\Telemetry\Config\TelemetryConfig;
use App\Framework\Telemetry\Exporters\FileExporter;
use App\Framework\Telemetry\Exporters\PrometheusExporter;
use App\Framework\Telemetry\UnifiedTelemetryService;
use PHPUnit\Framework\TestCase;

/**
 * Tests for the Telemetry module
 */
class TelemetryTest extends TestCase
{
    private UnifiedTelemetryService $telemetryService;

    private FileExporter $fileExporter;

    private PrometheusExporter $prometheusExporter;

    private string $tempDir;

    protected function setUp(): void
    {
        parent::setUp();

        // Create a temporary directory for telemetry files
        $this->tempDir = sys_get_temp_dir() . '/telemetry-test-' . uniqid();
        mkdir($this->tempDir, 0755, true);

        // Create dependencies
        $clock = new SystemClock();
        $logger = new DefaultLogger();
        $performanceCollector = new EnhancedPerformanceCollector($clock);
        $circuitBreaker = new CircuitBreaker($logger);

        // Create exporters
        $this->fileExporter = new FileExporter($this->tempDir, true);
        $this->prometheusExporter = new PrometheusExporter();

        // Create config
        $config = new TelemetryConfig(
            serviceName: 'test-service',
            serviceVersion: '1.0.0',
            environment: 'test',
            enabled: true,
            samplingRatio: 1.0
        );

        // Create telemetry service
        $this->telemetryService = new UnifiedTelemetryService(
            $performanceCollector,
            $circuitBreaker,
            $logger,
            $clock,
            $config
        );

        // Add exporters
        $this->telemetryService->addExporter($this->fileExporter);
        $this->telemetryService->addExporter($this->prometheusExporter);
    }

    protected function tearDown(): void
    {
        // Clean up temporary directory
        $this->removeDirectory($this->tempDir);

        parent::tearDown();
    }

    /**
     * Test that operations can be created and exported
     */
    public function testOperations(): void
    {
        // Start an operation
        $operation = $this->telemetryService->startOperation(
            'test_operation',
            'test',
            ['test_attribute' => 'test_value']
        );

        // Add an attribute
        $operation->addAttribute('another_attribute', 'another_value');

        // End the operation
        $operation->end('success');

        // Flush exporters
        $this->telemetryService->flush();

        // Check that the operation was exported to the file
        $date = date('Y-m-d');
        $operationsFile = "{$this->tempDir}/operations-{$date}.jsonl";
        $this->assertFileExists($operationsFile);

        $content = file_get_contents($operationsFile);
        $this->assertStringContainsString('test_operation', $content);
        $this->assertStringContainsString('test_attribute', $content);
        $this->assertStringContainsString('another_attribute', $content);
        $this->assertStringContainsString('success', $content);
    }

    /**
     * Test that metrics can be recorded and exported
     */
    public function testMetrics(): void
    {
        // Record a metric
        $this->telemetryService->recordMetric(
            'test_metric',
            42.0,
            'units',
            ['test_attribute' => 'test_value']
        );

        // Flush exporters
        $this->telemetryService->flush();

        // Check that the metric was exported to the file
        $date = date('Y-m-d');
        $metricsFile = "{$this->tempDir}/metrics-{$date}.jsonl";
        $this->assertFileExists($metricsFile);

        $content = file_get_contents($metricsFile);
        $this->assertStringContainsString('test_metric', $content);
        $this->assertStringContainsString('42', $content);
        $this->assertStringContainsString('units', $content);
        $this->assertStringContainsString('test_attribute', $content);

        // Check Prometheus output
        $prometheusOutput = $this->prometheusExporter->getMetricsOutput();
        $this->assertStringContainsString('test_metric', $prometheusOutput);
        $this->assertStringContainsString('42', $prometheusOutput);
    }

    /**
     * Test that events can be recorded and exported
     */
    public function testEvents(): void
    {
        // Record an event
        $this->telemetryService->recordEvent(
            'test_event',
            ['test_attribute' => 'test_value'],
            'info'
        );

        // Flush exporters
        $this->telemetryService->flush();

        // Check that the event was exported to the file
        $date = date('Y-m-d');
        $eventsFile = "{$this->tempDir}/events-{$date}.jsonl";
        $this->assertFileExists($eventsFile);

        $content = file_get_contents($eventsFile);
        $this->assertStringContainsString('test_event', $content);
        $this->assertStringContainsString('info', $content);
        $this->assertStringContainsString('test_attribute', $content);

        // Check Prometheus output (events are exported as counters)
        $prometheusOutput = $this->prometheusExporter->getMetricsOutput();
        $this->assertStringContainsString('events_info_total', $prometheusOutput);
    }

    /**
     * Test the trace method
     */
    public function testTrace(): void
    {
        // Use the trace method
        $result = $this->telemetryService->trace(
            'test_trace',
            'test',
            function () {
                return 'test_result';
            },
            ['test_attribute' => 'test_value']
        );

        // Check the result
        $this->assertEquals('test_result', $result);

        // Flush exporters
        $this->telemetryService->flush();

        // Check that the operation was exported to the file
        $date = date('Y-m-d');
        $operationsFile = "{$this->tempDir}/operations-{$date}.jsonl";
        $this->assertFileExists($operationsFile);

        $content = file_get_contents($operationsFile);
        $this->assertStringContainsString('test_trace', $content);
        $this->assertStringContainsString('test_attribute', $content);
        $this->assertStringContainsString('success', $content);
    }

    /**
     * Test error handling in operations
     */
    public function testErrorHandling(): void
    {
        // Start an operation
        $operation = $this->telemetryService->startOperation(
            'error_operation',
            'test',
            ['test_attribute' => 'test_value']
        );

        // Mark as failed
        $operation->fail('Test error message');

        // Flush exporters
        $this->telemetryService->flush();

        // Check that the operation was exported to the file
        $date = date('Y-m-d');
        $operationsFile = "{$this->tempDir}/operations-{$date}.jsonl";
        $this->assertFileExists($operationsFile);

        $content = file_get_contents($operationsFile);
        $this->assertStringContainsString('error_operation', $content);
        $this->assertStringContainsString('error', $content);
        $this->assertStringContainsString('Test error message', $content);
    }

    /**
     * Helper method to recursively remove a directory
     */
    private function removeDirectory(string $dir): void
    {
        if (! is_dir($dir)) {
            return;
        }

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

        rmdir($dir);
    }
}
