<?php

declare(strict_types=1);

use App\Framework\Database\ValueObjects\SqlState;

describe('SqlState Value Object', function () {
    it('validates SQLSTATE format', function () {
        // Valid 5-character alphanumeric codes
        expect(fn() => new SqlState('23505'))->not->toThrow(\InvalidArgumentException::class);
        expect(fn() => new SqlState('08001'))->not->toThrow(\InvalidArgumentException::class);
        expect(fn() => new SqlState('42S02'))->not->toThrow(\InvalidArgumentException::class);
        expect(fn() => new SqlState('HY000'))->not->toThrow(\InvalidArgumentException::class);
    });

    it('rejects invalid SQLSTATE formats', function () {
        // Too short
        expect(fn() => new SqlState('2350'))->toThrow(\InvalidArgumentException::class);

        // Too long
        expect(fn() => new SqlState('235050'))->toThrow(\InvalidArgumentException::class);

        // Invalid characters
        expect(fn() => new SqlState('23-05'))->toThrow(\InvalidArgumentException::class);
        expect(fn() => new SqlState('23@05'))->toThrow(\InvalidArgumentException::class);
        expect(fn() => new SqlState('abcde'))->toThrow(\InvalidArgumentException::class);
    });

    it('extracts SQLSTATE class correctly', function () {
        $sqlState = new SqlState('23505');
        expect($sqlState->getClass())->toBe('23');

        $sqlState = new SqlState('08001');
        expect($sqlState->getClass())->toBe('08');

        $sqlState = new SqlState('42S02');
        expect($sqlState->getClass())->toBe('42');
    });

    it('extracts SQLSTATE subclass correctly', function () {
        $sqlState = new SqlState('23505');
        expect($sqlState->getSubclass())->toBe('505');

        $sqlState = new SqlState('08001');
        expect($sqlState->getSubclass())->toBe('001');

        $sqlState = new SqlState('42S02');
        expect($sqlState->getSubclass())->toBe('S02');
    });

    it('identifies connection errors', function () {
        $connectionError = new SqlState('08001');
        expect($connectionError->isConnectionError())->toBeTrue();

        $otherError = new SqlState('23505');
        expect($otherError->isConnectionError())->toBeFalse();
    });

    it('identifies constraint violations', function () {
        $constraintViolation = new SqlState('23505');
        expect($constraintViolation->isConstraintViolation())->toBeTrue();

        $otherError = new SqlState('08001');
        expect($otherError->isConstraintViolation())->toBeFalse();
    });

    it('identifies transaction rollbacks', function () {
        $transactionRollback = new SqlState('40001');
        expect($transactionRollback->isTransactionRollback())->toBeTrue();

        $otherError = new SqlState('23505');
        expect($otherError->isTransactionRollback())->toBeFalse();
    });

    it('identifies syntax errors', function () {
        $syntaxError = new SqlState('42000');
        expect($syntaxError->isSyntaxError())->toBeTrue();

        $otherError = new SqlState('23505');
        expect($otherError->isSyntaxError())->toBeFalse();
    });

    it('identifies driver errors', function () {
        $driverError = new SqlState('HY000');
        expect($driverError->isDriverError())->toBeTrue();

        $otherError = new SqlState('23505');
        expect($otherError->isDriverError())->toBeFalse();
    });

    it('identifies unique violations', function () {
        $uniqueViolation = new SqlState('23505');
        expect($uniqueViolation->isUniqueViolation())->toBeTrue();

        $otherConstraint = new SqlState('23503');
        expect($otherConstraint->isUniqueViolation())->toBeFalse();
    });

    it('identifies foreign key violations', function () {
        $foreignKeyViolation = new SqlState('23503');
        expect($foreignKeyViolation->isForeignKeyViolation())->toBeTrue();

        $otherConstraint = new SqlState('23505');
        expect($otherConstraint->isForeignKeyViolation())->toBeFalse();
    });

    it('identifies not null violations', function () {
        $notNullViolation = new SqlState('23502');
        expect($notNullViolation->isNotNullViolation())->toBeTrue();

        $otherConstraint = new SqlState('23505');
        expect($otherConstraint->isNotNullViolation())->toBeFalse();
    });

    it('identifies check constraint violations', function () {
        $checkViolation = new SqlState('23514');
        expect($checkViolation->isCheckViolation())->toBeTrue();

        $otherConstraint = new SqlState('23505');
        expect($otherConstraint->isCheckViolation())->toBeFalse();
    });

    it('identifies table not found errors', function () {
        $tableNotFound = new SqlState('42S02');
        expect($tableNotFound->isTableNotFound())->toBeTrue();

        $otherError = new SqlState('42S22');
        expect($otherError->isTableNotFound())->toBeFalse();
    });

    it('identifies column not found errors', function () {
        $columnNotFound = new SqlState('42S22');
        expect($columnNotFound->isColumnNotFound())->toBeTrue();

        $otherError = new SqlState('42S02');
        expect($otherError->isColumnNotFound())->toBeFalse();
    });

    it('identifies deadlocks', function () {
        $deadlock = new SqlState('40001');
        expect($deadlock->isDeadlock())->toBeTrue();

        $otherError = new SqlState('40002');
        expect($otherError->isDeadlock())->toBeFalse();
    });

    it('identifies serialization failures', function () {
        $serializationFailure = new SqlState('40001');
        expect($serializationFailure->isSerializationFailure())->toBeTrue();

        $otherError = new SqlState('40002');
        expect($otherError->isSerializationFailure())->toBeFalse();
    });

    it('identifies connection failures', function () {
        $connectionFailure = new SqlState('08001');
        expect($connectionFailure->isConnectionFailure())->toBeTrue();

        $otherConnectionError = new SqlState('08003');
        expect($otherConnectionError->isConnectionFailure())->toBeFalse();
    });

    it('identifies connection does not exist', function () {
        $connectionDoesNotExist = new SqlState('08003');
        expect($connectionDoesNotExist->isConnectionDoesNotExist())->toBeTrue();

        $otherConnectionError = new SqlState('08001');
        expect($otherConnectionError->isConnectionDoesNotExist())->toBeFalse();
    });

    it('identifies connection rejected', function () {
        $connectionRejected = new SqlState('08004');
        expect($connectionRejected->isConnectionRejected())->toBeTrue();

        $otherConnectionError = new SqlState('08001');
        expect($otherConnectionError->isConnectionRejected())->toBeFalse();
    });

    it('compares SQLSTATE codes for equality', function () {
        $sqlState1 = new SqlState('23505');
        $sqlState2 = new SqlState('23505');
        $sqlState3 = new SqlState('23503');

        expect($sqlState1->equals($sqlState2))->toBeTrue();
        expect($sqlState1->equals($sqlState3))->toBeFalse();
    });

    it('converts to string representation', function () {
        $sqlState = new SqlState('23505');

        expect($sqlState->toString())->toBe('23505');
        expect((string) $sqlState)->toBe('23505');
        expect($sqlState->__toString())->toBe('23505');
    });

    it('handles PostgreSQL-specific codes', function () {
        $pgUniqueViolation = new SqlState('23505');
        expect($pgUniqueViolation->isUniqueViolation())->toBeTrue();
        expect($pgUniqueViolation->isConstraintViolation())->toBeTrue();

        $pgTableNotFound = new SqlState('42P01');
        expect($pgTableNotFound->isSyntaxError())->toBeTrue();
    });

    it('handles MySQL-specific codes', function () {
        $mysqlTableNotFound = new SqlState('42S02');
        expect($mysqlTableNotFound->isTableNotFound())->toBeTrue();
        expect($mysqlTableNotFound->isSyntaxError())->toBeTrue();

        $mysqlColumnNotFound = new SqlState('42S22');
        expect($mysqlColumnNotFound->isColumnNotFound())->toBeTrue();
    });

    it('handles driver-specific codes', function () {
        $driverError = new SqlState('HY000');
        expect($driverError->isDriverError())->toBeTrue();
        expect($driverError->getClass())->toBe('HY');
    });
});
