<?php

declare(strict_types=1);

use App\Framework\QrCode\QrCodeGenerator;
use App\Framework\QrCode\ValueObjects\EncodingMode;
use App\Framework\QrCode\ValueObjects\ErrorCorrectionLevel;
use App\Framework\QrCode\ValueObjects\QrCodeConfig;
use App\Framework\QrCode\ValueObjects\QrCodeVersion;

test('can generate QR code with default config', function () {
    $data = 'Hello World';

    $matrix = QrCodeGenerator::generate($data);

    expect($matrix)->toBeInstanceOf(\App\Framework\QrCode\ValueObjects\QrCodeMatrix::class)
        ->and($matrix->getSize())->toBeGreaterThan(0)
        ->and($matrix->getVersion()->getVersionNumber())->toBe(1);
});

test('can generate QR code with auto-size config', function () {
    $shortData = 'Test';
    $longData = str_repeat('A', 30);

    $matrix1 = QrCodeGenerator::generate($shortData);
    $matrix2 = QrCodeGenerator::generate($longData);

    expect($matrix1->getVersion()->getVersionNumber())->toBe(1)
        ->and($matrix2->getVersion()->getVersionNumber())->toBeGreaterThanOrEqual(2);
});

test('can generate QR code with explicit version', function () {
    $data = 'Test';
    $config = QrCodeConfig::withVersion(2);

    $matrix = QrCodeGenerator::generate($data, $config);

    expect($matrix->getVersion()->getVersionNumber())->toBe(2)
        ->and($matrix->getSize())->toBe(25); // Version 2 = 25x25
});

test('generates proper matrix size for different versions', function () {
    $data = 'Test';

    $matrix1 = QrCodeGenerator::generate($data, QrCodeConfig::withVersion(1));
    $matrix2 = QrCodeGenerator::generate($data, QrCodeConfig::withVersion(2));
    $matrix3 = QrCodeGenerator::generate($data, QrCodeConfig::withVersion(3));

    expect($matrix1->getSize())->toBe(21) // Version 1: 21x21
        ->and($matrix2->getSize())->toBe(25) // Version 2: 25x25
        ->and($matrix3->getSize())->toBe(29); // Version 3: 29x29
});

test('matrix contains finder patterns', function () {
    $data = 'Test';
    $matrix = QrCodeGenerator::generate($data);

    // Check top-left finder pattern (corner module should be dark)
    expect($matrix->getModuleAt(0, 0)->isDark())->toBeTrue()
        ->and($matrix->getModuleAt(6, 6)->isDark())->toBeTrue();
});

test('matrix contains timing patterns', function () {
    $data = 'Test';
    $matrix = QrCodeGenerator::generate($data);

    // Row 6 should have alternating modules (timing pattern)
    expect($matrix->getModuleAt(6, 8)->isDark())->toBeTrue()
        ->and($matrix->getModuleAt(6, 9)->isLight())->toBeTrue()
        ->and($matrix->getModuleAt(6, 10)->isDark())->toBeTrue();
});

test('throws exception when data too long for version', function () {
    $data = str_repeat('A', 100); // Too long for version 1-3

    QrCodeGenerator::generate($data);
})->throws(\App\Framework\Exception\FrameworkException::class);

test('matrix has correct dark module count', function () {
    $data = 'Test';
    $matrix = QrCodeGenerator::generate($data);

    $darkCount = $matrix->countDarkModules();

    // Matrix should have some dark modules (at least finder patterns)
    expect($darkCount)->toBeGreaterThan(50) // Finder patterns alone = ~63 modules
        ->and($darkCount)->toBeLessThan($matrix->getSize() * $matrix->getSize());
});

test('can generate ASCII representation', function () {
    $data = 'A';
    $matrix = QrCodeGenerator::generate($data);

    $ascii = $matrix->toAsciiArt();

    expect($ascii)->toBeString()
        ->and($ascii)->toContain('█') // Should contain dark modules
        ->and($ascii)->toContain('░'); // Should contain light modules
});

test('can generate binary representation', function () {
    $data = 'A';
    $matrix = QrCodeGenerator::generate($data);

    $binary = $matrix->toBinaryString();

    expect($binary)->toBeString()
        ->and($binary)->toContain('1') // Should contain 1s (dark)
        ->and($binary)->toContain('0'); // Should contain 0s (light)
});

test('generates deterministic output for same input', function () {
    $data = 'Test123';

    $matrix1 = QrCodeGenerator::generate($data);
    $matrix2 = QrCodeGenerator::generate($data);

    expect($matrix1->toBinaryString())->toBe($matrix2->toBinaryString());
});

test('supports different data types', function () {
    // Numeric-like data (but still byte mode in Phase 1)
    $numeric = '1234567890';
    $matrix1 = QrCodeGenerator::generate($numeric);

    // Alphanumeric-like data
    $alphanumeric = 'HELLO WORLD 123';
    $matrix2 = QrCodeGenerator::generate($alphanumeric);

    // Special characters
    $special = 'test@example.com';
    $matrix3 = QrCodeGenerator::generate($special);

    // UTF-8
    $utf8 = 'Hëllö Wörld';
    $matrix4 = QrCodeGenerator::generate($utf8);

    expect($matrix1)->toBeInstanceOf(\App\Framework\QrCode\ValueObjects\QrCodeMatrix::class)
        ->and($matrix2)->toBeInstanceOf(\App\Framework\QrCode\ValueObjects\QrCodeMatrix::class)
        ->and($matrix3)->toBeInstanceOf(\App\Framework\QrCode\ValueObjects\QrCodeMatrix::class)
        ->and($matrix4)->toBeInstanceOf(\App\Framework\QrCode\ValueObjects\QrCodeMatrix::class);
});
