<?php

declare(strict_types=1);

use App\Framework\Core\ValueObjects\Byte;
use App\Framework\LiveComponents\ValueObjects\ChunkHash;
use App\Framework\LiveComponents\ValueObjects\ChunkMetadata;

describe('ChunkMetadata Value Object', function () {
    it('creates chunk metadata', function () {
        $hash = ChunkHash::fromData('test chunk');
        $size = Byte::fromKilobytes(100);

        $metadata = ChunkMetadata::create(
            index: 0,
            size: $size,
            hash: $hash
        );

        expect($metadata->index)->toBe(0);
        expect($metadata->size)->toBe($size);
        expect($metadata->hash)->toBe($hash);
        expect($metadata->uploaded)->toBeFalse();
    });

    it('rejects negative chunk index', function () {
        $hash = ChunkHash::fromData('test');
        $size = Byte::fromKilobytes(100);

        expect(fn() => ChunkMetadata::create(-1, $size, $hash))
            ->toThrow(InvalidArgumentException::class);
    });

    it('rejects zero chunk size', function () {
        $hash = ChunkHash::fromData('test');
        $size = Byte::fromBytes(0);

        expect(fn() => ChunkMetadata::create(0, $size, $hash))
            ->toThrow(InvalidArgumentException::class);
    });

    it('marks chunk as uploaded', function () {
        $hash = ChunkHash::fromData('test');
        $size = Byte::fromKilobytes(100);
        $metadata = ChunkMetadata::create(0, $size, $hash);

        expect($metadata->isUploaded())->toBeFalse();

        $uploaded = $metadata->withUploaded();

        expect($uploaded->isUploaded())->toBeTrue();
        expect($uploaded->index)->toBe($metadata->index);
        expect($uploaded->size)->toBe($metadata->size);
        expect($uploaded->hash)->toBe($metadata->hash);
    });

    it('preserves immutability when marking uploaded', function () {
        $hash = ChunkHash::fromData('test');
        $size = Byte::fromKilobytes(100);
        $original = ChunkMetadata::create(0, $size, $hash);

        $uploaded = $original->withUploaded();

        expect($original->isUploaded())->toBeFalse();
        expect($uploaded->isUploaded())->toBeTrue();
    });

    it('converts to array correctly', function () {
        $hash = ChunkHash::fromData('test chunk data');
        $size = Byte::fromKilobytes(100);
        $metadata = ChunkMetadata::create(5, $size, $hash);

        $array = $metadata->toArray();

        expect($array['index'])->toBe(5);
        expect($array['size'])->toBe(102400); // 100KB in bytes
        expect($array['size_human'])->toBeString();
        expect($array['hash'])->toBe($hash->toString());
        expect($array['uploaded'])->toBeFalse();
    });

    it('includes uploaded status in array', function () {
        $hash = ChunkHash::fromData('test');
        $size = Byte::fromKilobytes(100);
        $metadata = ChunkMetadata::create(0, $size, $hash)->withUploaded();

        $array = $metadata->toArray();

        expect($array['uploaded'])->toBeTrue();
    });

    it('handles different chunk sizes', function () {
        $hash = ChunkHash::fromData('test');

        $small = ChunkMetadata::create(0, Byte::fromBytes(1), $hash);
        $medium = ChunkMetadata::create(1, Byte::fromMegabytes(5), $hash);
        $large = ChunkMetadata::create(2, Byte::fromMegabytes(100), $hash);

        expect($small->size->toBytes())->toBe(1);
        expect($medium->size->toBytes())->toBe(5242880); // 5MB
        expect($large->size->toBytes())->toBe(104857600); // 100MB
    });

    it('maintains chunk index ordering', function () {
        $hash = ChunkHash::fromData('test');
        $size = Byte::fromKilobytes(100);

        $chunks = [
            ChunkMetadata::create(0, $size, $hash),
            ChunkMetadata::create(5, $size, $hash),
            ChunkMetadata::create(10, $size, $hash),
        ];

        expect($chunks[0]->index)->toBe(0);
        expect($chunks[1]->index)->toBe(5);
        expect($chunks[2]->index)->toBe(10);
    });
});
