<?php

namespace MintyPHP\Tests\Http;

use MintyPHP\Http\ErrorLogger;
use PHPUnit\Framework\TestCase;

class ErrorLoggerTest extends TestCase
{
    private string $logFile;

    protected function setUp(): void
    {
        $this->logFile = 'storage/logs/errors/error-' . date('Y-m-d') . '.log';
        // Remove the file so each test starts clean.
        if (is_file($this->logFile)) {
            @unlink($this->logFile);
        }
    }

    public function testLogWritesJsonLineEntry(): void
    {
        $exception = new \RuntimeException('Test error', 42);
        $data = [
            'exception' => [
                'class' => 'RuntimeException',
                'message' => 'Test error',
                'code' => 42,
                'file' => '/app/web/index.php',
                'line' => 10,
                'trace' => [
                    ['file' => '/app/web/index.php', 'line' => 10],
                ],
            ],
            'request' => [
                'request_id' => 'abc-123',
                'channel' => 'web',
                'method' => 'GET',
                'url' => '/admin/dashboard?token=very-secret',
                'ip' => '127.0.0.1',
                'user_id' => 5,
                'tenant_id' => 1,
            ],
            'environment' => [
                'php_version' => '8.5.0',
                'memory_peak' => 12 * 1024 * 1024,
            ],
            'queries' => [],
        ];

        ErrorLogger::log($exception, $data);

        $this->assertFileExists($this->logFile);

        $lines = array_filter(explode("\n", (string) file_get_contents($this->logFile)));
        $this->assertCount(1, $lines);

        $entry = json_decode($lines[0], true);
        $this->assertIsArray($entry);
        $this->assertSame('error', $entry['level']);
        $this->assertSame('abc-123', $entry['request_id']);
        $this->assertSame('RuntimeException', $entry['exception']['class']);
        $this->assertSame('[REDACTED]', $entry['exception']['message']);
        $this->assertArrayHasKey('message_hash', $entry['exception']);
        $this->assertStringStartsWith('sha256:', (string) $entry['exception']['message_hash']);
        $this->assertArrayHasKey('ip_hash', $entry['request']);
        $this->assertStringStartsWith('sha256:', $entry['request']['ip_hash']);
        $this->assertSame('/admin/dashboard', $entry['request']['path']);
        $this->assertArrayNotHasKey('url', $entry['request']);
        // Raw IP should NOT be in the log.
        $this->assertStringNotContainsString('127.0.0.1', json_encode($entry));
        $this->assertStringNotContainsString('very-secret', json_encode($entry));
    }

    public function testLogStoresPathWithoutQueryParameters(): void
    {
        $exception = new \RuntimeException('Query redaction');
        $data = [
            'request' => [
                'request_id' => 'query-test',
                'url' => '/auth/reset-password?email=user@example.com&token=abc123',
            ],
            'exception' => [],
            'environment' => [],
        ];

        ErrorLogger::log($exception, $data);

        $this->assertFileExists($this->logFile);
        $lines = array_filter(explode("\n", (string) file_get_contents($this->logFile)));
        $this->assertCount(1, $lines);

        $entry = json_decode($lines[0], true);
        $this->assertIsArray($entry);
        $this->assertSame('/auth/reset-password', $entry['request']['path']);
        $this->assertStringNotContainsString('user@example.com', $lines[0]);
        $this->assertStringNotContainsString('token=abc123', $lines[0]);
    }

    public function testLogRedactsIpAddress(): void
    {
        $exception = new \RuntimeException('IP test');
        $data = [
            'request' => ['ip' => '192.168.1.42', 'request_id' => 'test-id'],
            'exception' => [],
            'environment' => [],
        ];

        ErrorLogger::log($exception, $data);

        $this->assertFileExists($this->logFile);
        $contents = (string) file_get_contents($this->logFile);
        $this->assertStringNotContainsString('192.168.1.42', $contents);
        $this->assertStringContainsString('sha256:', $contents);
    }

    public function testLogNeverThrowsOnFailure(): void
    {
        // Even with broken data, log() must not throw.
        $exception = new \RuntimeException('safe test');
        ErrorLogger::log($exception, []);

        self::addToAssertionCount(1);
    }
}
