<?php

declare(strict_types=1);

namespace App\Framework\Core\ValueObjects;

use InvalidArgumentException;

/**
 * Immutable PHP namespace value object with validation and operations
 */
final readonly class PhpNamespace
{
    private string $namespace;

    private function __construct(string $namespace)
    {
        // Empty namespace is valid (global namespace)
        if ($namespace === '') {
            $this->namespace = '';
            return;
        }

        // Remove leading/trailing backslashes
        $namespace = trim($namespace, '\\');

        if (! $this->isValidNamespace($namespace)) {
            throw new InvalidArgumentException("Invalid namespace: {$namespace}");
        }

        $this->namespace = $namespace;
    }

    /**
     * Create from namespace string
     */
    public static function fromString(string $namespace): self
    {
        return new self($namespace);
    }

    /**
     * Create from class name
     */
    public static function fromClass(string $className): self
    {
        $className = ltrim($className, '\\');
        $lastBackslash = strrpos($className, '\\');

        if ($lastBackslash === false) {
            return new self(''); // Global namespace
        }

        return new self(substr($className, 0, $lastBackslash));
    }

    /**
     * Create from namespace parts
     * @param array<string> $parts
     */
    public static function fromParts(array $parts): self
    {
        return new self(implode('\\', $parts));
    }

    /**
     * Create global namespace
     */
    public static function global(): self
    {
        return new self('');
    }

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

    /**
     * Get namespace parts
     * @return array<string>
     */
    public function parts(): array
    {
        if ($this->namespace === '') {
            return [];
        }

        return explode('\\', $this->namespace);
    }

    /**
     * Get namespace depth (number of levels)
     */
    public function depth(): int
    {
        if ($this->namespace === '') {
            return 0;
        }

        return count($this->parts());
    }

    /**
     * Check if this is the global namespace
     */
    public function isGlobal(): bool
    {
        return $this->namespace === '';
    }

    /**
     * Get parent namespace
     */
    public function parent(): ?self
    {
        $parts = $this->parts();

        if (count($parts) <= 1) {
            return null; // No parent or already at global namespace
        }

        array_pop($parts);

        return self::fromParts($parts);
    }

    /**
     * Append namespace segment
     */
    public function append(string $segment): self
    {
        if ($this->namespace === '') {
            return new self($segment);
        }

        return new self($this->namespace . '\\' . ltrim($segment, '\\'));
    }

    /**
     * Check if namespace starts with given prefix
     */
    public function startsWith(string|self $prefix): bool
    {
        $prefixStr = $prefix instanceof self ? $prefix->toString() : $prefix;
        $prefixStr = trim($prefixStr, '\\');

        if ($prefixStr === '') {
            return true; // All namespaces start with global namespace
        }

        return str_starts_with($this->namespace, $prefixStr);
    }

    /**
     * Check if namespace ends with given suffix
     */
    public function endsWith(string|self $suffix): bool
    {
        $suffixStr = $suffix instanceof self ? $suffix->toString() : $suffix;
        $suffixStr = trim($suffixStr, '\\');

        if ($suffixStr === '') {
            return true;
        }

        return str_ends_with($this->namespace, $suffixStr);
    }

    /**
     * Compare for equality
     */
    public function equals(self $other): bool
    {
        return $this->namespace === $other->namespace;
    }

    /**
     * Convert to a fully qualified class name (with leading backslash)
     */
    public function toFqcn(string $className): string
    {
        if ($this->namespace === '') {
            return '\\' . $className;
        }

        return '\\' . $this->namespace . '\\' . $className;
    }

    /**
     * Convert to file path (for PSR-4 autoloading)
     */
    public function toPath(): string
    {
        if ($this->namespace === '') {
            return '';
        }

        return str_replace('\\', '/', $this->namespace);
    }

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

    /**
     * Validate namespace format
     */
    private function isValidNamespace(string $namespace): bool
    {
        // Empty namespace is valid (global namespace)
        if ($namespace === '') {
            return true;
        }

        // Each part must be a valid PHP identifier
        $parts = explode('\\', $namespace);

        foreach ($parts as $part) {
            // Must start with letter or underscore, followed by letters, numbers, or underscores
            if (! preg_match('/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/', $part)) {
                return false;
            }
        }

        return true;
    }
}
