<?php

declare(strict_types=1);

namespace App\Framework\Database\ValueObjects;

use App\Framework\Database\Validation\DatabaseIdentifierValidator;
use InvalidArgumentException;

/**
 * Value Object representing a validated database name
 *
 * Ensures database names follow SQL naming conventions:
 * - Start with letter or underscore
 * - Contain only alphanumeric characters and underscores
 * - Maximum 64 characters (MySQL/PostgreSQL standard)
 * - No SQL injection patterns
 *
 * Immutable and type-safe replacement for string database names.
 */
final readonly class DatabaseName
{
    /**
     * @param string $value Validated database name
     * @throws InvalidArgumentException if name is invalid
     */
    private function __construct(
        public string $value
    ) {}

    /**
     * Create DatabaseName from string
     *
     * @throws InvalidArgumentException if name is invalid
     */
    public static function fromString(string $name): self
    {
        $validator = new DatabaseIdentifierValidator();
        $validator->validate($name, 'database');

        return new self($name);
    }

    /**
     * Get database name as string
     */
    public function toString(): string
    {
        return $this->value;
    }

    /**
     * Get quoted database name for SQL
     *
     * @param string $platform Database platform (mysql, postgresql, sqlite)
     */
    public function quoted(string $platform = 'mysql'): string
    {
        return match (strtolower($platform)) {
            'postgresql', 'postgres', 'pgsql' => '"' . $this->value . '"',
            'sqlite' => '"' . $this->value . '"',
            default => '`' . $this->value . '`', // MySQL default
        };
    }

    /**
     * Check if database name equals another
     *
     * Comparison is case-insensitive for database names
     */
    public function equals(self $other): bool
    {
        return strcasecmp($this->value, $other->value) === 0;
    }

    /**
     * Check if database name matches a pattern
     *
     * Pattern supports wildcards (*) for matching
     * Example: 'app_*' matches 'app_production', 'app_staging'
     */
    public function matches(string $pattern): bool
    {
        // Replace * wildcards with placeholder, quote the pattern, then restore wildcards
        $placeholder = '___WILDCARD___';
        $pattern = str_replace('*', $placeholder, $pattern);
        $regex = '/^' . preg_quote($pattern, '/') . '$/i';
        $regex = str_replace($placeholder, '.*', $regex);

        return preg_match($regex, $this->value) === 1;
    }

    /**
     * Check if database name is a reserved SQL keyword
     */
    public function isReservedKeyword(): bool
    {
        $validator = new DatabaseIdentifierValidator();
        return $validator->isReservedKeyword($this->value);
    }

    /**
     * Convert to lowercase
     */
    public function toLower(): string
    {
        return strtolower($this->value);
    }

    /**
     * Convert to uppercase
     */
    public function toUpper(): string
    {
        return strtoupper($this->value);
    }

    /**
     * Check if database name has a prefix
     */
    public function hasPrefix(string $prefix): bool
    {
        return str_starts_with($this->value, $prefix);
    }

    /**
     * Check if database name has a suffix
     */
    public function hasSuffix(string $suffix): bool
    {
        return str_ends_with($this->value, $suffix);
    }

    /**
     * Add prefix to database name (returns new instance)
     */
    public function withPrefix(string $prefix): self
    {
        return self::fromString($prefix . $this->value);
    }

    /**
     * Remove prefix from database name (returns new instance)
     */
    public function withoutPrefix(string $prefix): self
    {
        if (!$this->hasPrefix($prefix)) {
            return $this; // Optimization: return same instance if no change
        }

        return self::fromString(substr($this->value, strlen($prefix)));
    }

    /**
     * Add suffix to database name (returns new instance)
     */
    public function withSuffix(string $suffix): self
    {
        return self::fromString($this->value . $suffix);
    }

    /**
     * Remove suffix from database name (returns new instance)
     */
    public function withoutSuffix(string $suffix): self
    {
        if (!$this->hasSuffix($suffix)) {
            return $this; // Optimization: return same instance if no change
        }

        return self::fromString(substr($this->value, 0, -strlen($suffix)));
    }

    /**
     * Detect environment-based database names
     *
     * Returns the environment suffix if detected (e.g., 'production', 'staging', 'test')
     */
    public function getEnvironmentSuffix(): ?string
    {
        $environments = ['production', 'staging', 'development', 'test', 'local'];

        foreach ($environments as $env) {
            if ($this->hasSuffix('_' . $env)) {
                return $env;
            }
        }

        return null;
    }

    /**
     * Check if database name is for testing environment
     */
    public function isTestDatabase(): bool
    {
        return $this->hasSuffix('_test') || $this->hasPrefix('test_');
    }

    /**
     * Magic method for string conversion
     */
    public function __toString(): string
    {
        return $this->value;
    }
}
