<?php

declare(strict_types=1);

use App\Domain\QrCode\Exception\QrCodeException;
use App\Domain\QrCode\QrCode;
use App\Domain\QrCode\Service\QrCodeEncoder;
use App\Domain\QrCode\Service\QrCodeGenerator;
use App\Domain\QrCode\Service\QrCodeMasker;
use App\Domain\QrCode\Service\QrCodeRenderer;
use App\Domain\QrCode\Service\ReedSolomon;
use App\Domain\QrCode\ValueObject\ErrorCorrectionLevel;
use App\Domain\QrCode\ValueObject\MaskPattern;
use App\Domain\QrCode\ValueObject\QrCodeVersion;

test('QrCode Objekt kann erstellt werden', function () {
    $qrCode = new QrCode('Testdaten');

    expect($qrCode->getData())->toBe('Testdaten');
    expect($qrCode->getErrorCorrectionLevel())->toBe(ErrorCorrectionLevel::M);
    expect($qrCode->getVersion())->toBeNull();
});

test('QrCode wirft Exception bei leeren Daten', function () {
    expect(fn () => new QrCode(''))->toThrow(QrCodeException::class);
});

test('QrCode withMethods erzeugen neue Instanzen', function () {
    $original = new QrCode('Test');
    $withLevel = $original->withErrorCorrectionLevel(ErrorCorrectionLevel::H);
    $withVersion = $original->withVersion(new QrCodeVersion(5));

    expect($withLevel)->not->toBe($original);
    expect($withVersion)->not->toBe($original);
    expect($withLevel->getErrorCorrectionLevel())->toBe(ErrorCorrectionLevel::H);
    expect($withVersion->getVersion()->getValue())->toBe(5);
});

test('MaskPattern kann Maskenbedingungen korrekt auswerten', function () {
    $pattern = MaskPattern::PATTERN_0;

    expect($pattern->shouldMask(0, 0))->toBeTrue(); // (0+0)%2 = 0
    expect($pattern->shouldMask(1, 0))->toBeFalse(); // (1+0)%2 = 1
    expect($pattern->shouldMask(1, 1))->toBeTrue(); // (1+1)%2 = 0
});

test('QrCodeEncoder kann Daten kodieren', function () {
    $encoder = new QrCodeEncoder();
    $qrCode = new QrCode('123');
    $version = new QrCodeVersion(1);

    $encoded = $encoder->encode($qrCode, $version);

    expect($encoded)->toBeString();
    expect(strlen($encoded))->toBeGreaterThan(0);
});

test('QrCodeGenerator kann QR-Code-Matrix generieren', function () {
    $generator = new QrCodeGenerator(
        new QrCodeEncoder(),
        new QrCodeRenderer(new QrCodeMasker(), new ReedSolomon())
    );

    $qrCode = new QrCode('Test123');
    $matrix = $generator->generate($qrCode);

    expect($matrix->getSize())->toBeGreaterThan(20); // QR-Code ist mindestens Version 1 (21x21)
});

test('QrCodeMasker berechnet Maskenstrafen korrekt', function () {
    $masker = new QrCodeMasker();
    $size = 21; // Version 1

    // Matrix mit alternierenden Modulen (geringe Strafe)
    $alternatingMatrix = [];
    for ($i = 0; $i < $size; $i++) {
        $alternatingMatrix[$i] = [];
        for ($j = 0; $j < $size; $j++) {
            $alternatingMatrix[$i][$j] = ($i + $j) % 2 === 0;
        }
    }

    // Matrix mit vielen aufeinanderfolgenden gleichen Modulen (hohe Strafe)
    $repeatingMatrix = [];
    for ($i = 0; $i < $size; $i++) {
        $repeatingMatrix[$i] = [];
        for ($j = 0; $j < $size; $j++) {
            $repeatingMatrix[$i][$j] = $j < $size / 2;
        }
    }

    $alternatingPenalty = $masker->calculateMaskPenalty($alternatingMatrix, $size);
    $repeatingPenalty = $masker->calculateMaskPenalty($repeatingMatrix, $size);

    expect($alternatingPenalty)->toBeLessThan($repeatingPenalty);
});
