<?php

declare(strict_types=1);

namespace App\Framework\Database\ValueObjects;

/**
 * SQLSTATE Value Object
 *
 * Represents a 5-character SQLSTATE error code as defined by SQL standard.
 * Format: [Class(2 chars)][Subclass(3 chars)]
 *
 * Common SQLSTATE classes:
 * - 08: Connection exceptions
 * - 23: Integrity constraint violations
 * - 40: Transaction rollback
 * - 42: Syntax error or access violation
 * - HY: Driver-specific errors
 *
 * @see https://en.wikipedia.org/wiki/SQLSTATE
 */
final readonly class SqlState
{
    public function __construct(
        public string $code  // 5-character SQLSTATE code (e.g., "23505", "42S02")
    ) {
        if (!preg_match('/^[0-9A-Z]{5}$/', $code)) {
            throw new \InvalidArgumentException("Invalid SQLSTATE format: {$code}. Expected 5 alphanumeric characters.");
        }
    }

    /**
     * Get SQLSTATE class (first 2 characters)
     *
     * The class indicates the general category of error:
     * - 00: Successful completion
     * - 01: Warning
     * - 02: No data
     * - 08: Connection exception
     * - 23: Integrity constraint violation
     * - 40: Transaction rollback
     * - 42: Syntax error or access violation
     */
    public function getClass(): string
    {
        return substr($this->code, 0, 2);
    }

    /**
     * Get SQLSTATE subclass (last 3 characters)
     *
     * The subclass provides specific error information within the class.
     */
    public function getSubclass(): string
    {
        return substr($this->code, 2, 3);
    }

    /**
     * Check if this is a connection error (Class 08)
     */
    public function isConnectionError(): bool
    {
        return $this->getClass() === '08';
    }

    /**
     * Check if this is an integrity constraint violation (Class 23)
     */
    public function isConstraintViolation(): bool
    {
        return $this->getClass() === '23';
    }

    /**
     * Check if this is a transaction rollback (Class 40)
     */
    public function isTransactionRollback(): bool
    {
        return $this->getClass() === '40';
    }

    /**
     * Check if this is a syntax error or access violation (Class 42)
     */
    public function isSyntaxError(): bool
    {
        return $this->getClass() === '42';
    }

    /**
     * Check if this is a driver-specific error (Class HY)
     */
    public function isDriverError(): bool
    {
        return $this->getClass() === 'HY';
    }

    /**
     * Check if this is a unique constraint violation (23505)
     */
    public function isUniqueViolation(): bool
    {
        return $this->code === '23505';
    }

    /**
     * Check if this is a foreign key constraint violation (23503)
     */
    public function isForeignKeyViolation(): bool
    {
        return $this->code === '23503';
    }

    /**
     * Check if this is a not null constraint violation (23502)
     */
    public function isNotNullViolation(): bool
    {
        return $this->code === '23502';
    }

    /**
     * Check if this is a check constraint violation (23514)
     */
    public function isCheckViolation(): bool
    {
        return $this->code === '23514';
    }

    /**
     * Check if this is a table not found error (42S02)
     */
    public function isTableNotFound(): bool
    {
        return $this->code === '42S02';
    }

    /**
     * Check if this is a column not found error (42S22)
     */
    public function isColumnNotFound(): bool
    {
        return $this->code === '42S22';
    }

    /**
     * Check if this is a deadlock (40001)
     */
    public function isDeadlock(): bool
    {
        return $this->code === '40001';
    }

    /**
     * Check if this is a serialization failure (40001)
     * Alias for isDeadlock() for clarity
     */
    public function isSerializationFailure(): bool
    {
        return $this->code === '40001';
    }

    /**
     * Check if this is a connection failure (08001)
     */
    public function isConnectionFailure(): bool
    {
        return $this->code === '08001';
    }

    /**
     * Check if this is connection does not exist (08003)
     */
    public function isConnectionDoesNotExist(): bool
    {
        return $this->code === '08003';
    }

    /**
     * Check if this is connection rejected (08004)
     */
    public function isConnectionRejected(): bool
    {
        return $this->code === '08004';
    }

    /**
     * Check if two SQLSTATE codes are equal
     */
    public function equals(self $other): bool
    {
        return $this->code === $other->code;
    }

    /**
     * Convert to string representation
     */
    public function toString(): string
    {
        return $this->code;
    }

    /**
     * String representation for debugging
     */
    public function __toString(): string
    {
        return $this->code;
    }
}
