<?php

declare(strict_types=1);

namespace Tests\Framework\Health\ValueObjects;

use App\Framework\Health\ValueObjects\HealthDetails;
use InvalidArgumentException;

describe('HealthDetails Value Object', function () {
    it('can be created empty', function () {
        $details = HealthDetails::empty();

        expect($details->isEmpty())->toBeTrue();
        expect($details->count())->toBe(0);
        expect($details->toArray())->toBe([]);
    });

    it('can be created from array', function () {
        $data = ['host' => 'localhost', 'port' => 3306];
        $details = HealthDetails::fromArray($data);

        expect($details->isEmpty())->toBeFalse();
        expect($details->count())->toBe(2);
        expect($details->get('host'))->toBe('localhost');
        expect($details->get('port'))->toBe(3306);
    });

    it('can add and remove details immutably', function () {
        $details = HealthDetails::empty();
        $newDetails = $details->with('key', 'value');

        expect($details->has('key'))->toBeFalse(); // Original unchanged
        expect($newDetails->has('key'))->toBeTrue();
        expect($newDetails->get('key'))->toBe('value');

        $withoutKey = $newDetails->without('key');
        expect($withoutKey->has('key'))->toBeFalse();
        expect($newDetails->has('key'))->toBeTrue(); // Original unchanged
    });

    it('gets values with defaults', function () {
        $details = HealthDetails::fromArray(['existing' => 'value']);

        expect($details->get('existing'))->toBe('value');
        expect($details->get('missing'))->toBeNull();
        expect($details->get('missing', 'default'))->toBe('default');
    });

    it('creates database details correctly', function () {
        $details = HealthDetails::forDatabase('localhost', 3306, 'testdb', 25.5, 10);

        expect($details->get('host'))->toBe('localhost');
        expect($details->get('port'))->toBe(3306);
        expect($details->get('database'))->toBe('testdb');
        expect($details->get('connection_time_ms'))->toBe(25.5);
        expect($details->get('active_connections'))->toBe(10);
    });

    it('creates cache details correctly', function () {
        $details = HealthDetails::forCache('redis', 95, 1000, '128MB');

        expect($details->get('driver'))->toBe('redis');
        expect($details->get('hit_rate_percent'))->toBe(95);
        expect($details->get('total_keys'))->toBe(1000);
        expect($details->get('memory_usage'))->toBe('128MB');
    });

    it('creates disk details correctly', function () {
        $details = HealthDetails::forDisk('/var/log', '100GB', '25GB', 75.0);

        expect($details->get('path'))->toBe('/var/log');
        expect($details->get('total_space'))->toBe('100GB');
        expect($details->get('free_space'))->toBe('25GB');
        expect($details->get('usage_percent'))->toBe(75.0);
    });

    it('creates system details correctly', function () {
        $details = HealthDetails::forSystem(45.2, '8GB/16GB', 1.5, '5 days');

        expect($details->get('cpu_usage_percent'))->toBe(45.2);
        expect($details->get('memory_usage'))->toBe('8GB/16GB');
        expect($details->get('load_average'))->toBe(1.5);
        expect($details->get('uptime'))->toBe('5 days');
    });

    it('creates API details correctly', function () {
        $details = HealthDetails::forApi('/health', 200, 150.5, 'v1.2.3');

        expect($details->get('endpoint'))->toBe('/health');
        expect($details->get('status_code'))->toBe(200);
        expect($details->get('response_time_ms'))->toBe(150.5);
        expect($details->get('version'))->toBe('v1.2.3');
    });

    it('creates error details correctly', function () {
        $exception = new \RuntimeException('Test error', 500);
        $details = HealthDetails::forError($exception);

        expect($details->get('error_type'))->toBe('RuntimeException');
        expect($details->get('error_message'))->toBe('Test error');
        expect($details->get('error_code'))->toBe(500);
        expect($details->get('error_file'))->toBeString();
        expect($details->get('error_line'))->toBeInt();
    });

    it('can merge details', function () {
        $details1 = HealthDetails::fromArray(['key1' => 'value1']);
        $details2 = HealthDetails::fromArray(['key2' => 'value2']);

        $merged = $details1->merge($details2);

        expect($merged->get('key1'))->toBe('value1');
        expect($merged->get('key2'))->toBe('value2');
        expect($merged->count())->toBe(2);
    });

    it('identifies sensitive keys', function () {
        $details = HealthDetails::fromArray([
            'username' => 'john',
            'password' => 'secret123',
            'api_key' => 'abc123',
            'database_token' => 'xyz789',
            'normal_field' => 'value',
        ]);

        $sensitiveKeys = $details->getSensitiveKeys();

        expect($sensitiveKeys)->toContain('password');
        expect($sensitiveKeys)->toContain('api_key');
        expect($sensitiveKeys)->toContain('database_token');
        expect($sensitiveKeys)->not->toContain('username');
        expect($sensitiveKeys)->not->toContain('normal_field');
    });

    it('sanitizes sensitive data', function () {
        $details = HealthDetails::fromArray([
            'username' => 'john',
            'password' => 'secret123',
            'normal_field' => 'value',
        ]);

        $sanitized = $details->toSanitizedArray();

        expect($sanitized['username'])->toBe('john');
        expect($sanitized['password'])->toBe('[REDACTED]');
        expect($sanitized['normal_field'])->toBe('value');
    });

    it('throws exception for non-string keys', function () {
        expect(fn () => HealthDetails::fromArray([123 => 'value']))->toThrow(InvalidArgumentException::class);
    });

    it('throws exception for empty keys', function () {
        expect(fn () => HealthDetails::fromArray(['' => 'value']))->toThrow(InvalidArgumentException::class);
        expect(fn () => HealthDetails::fromArray(['   ' => 'value']))->toThrow(InvalidArgumentException::class);
    });

    it('throws exception for resource values', function () {
        $resource = fopen('php://memory', 'r');
        expect(fn () => HealthDetails::fromArray(['resource' => $resource]))->toThrow(InvalidArgumentException::class);
        fclose($resource);
    });

    it('allows scalar and null values', function () {
        $details = HealthDetails::fromArray([
            'string' => 'text',
            'int' => 123,
            'float' => 12.34,
            'bool' => true,
            'null' => null,
        ]);

        expect($details->get('string'))->toBe('text');
        expect($details->get('int'))->toBe(123);
        expect($details->get('float'))->toBe(12.34);
        expect($details->get('bool'))->toBeTrue();
        expect($details->get('null'))->toBeNull();
    });
});
