<?php

declare(strict_types=1);

namespace App\Framework\Core\ValueObjects;

use InvalidArgumentException;

/**
 * Represents a street address with number and name
 * Immutable value object for address handling
 */
final readonly class Street
{
    public function __construct(
        public string $name,
        public string $number,
        public ?string $addition = null
    ) {
        if (trim($name) === '') {
            throw new InvalidArgumentException('Street name cannot be empty');
        }

        if (trim($number) === '') {
            throw new InvalidArgumentException('Street number cannot be empty');
        }
    }

    public static function parse(string $streetAddress): self
    {
        // Try to parse common formats
        $patterns = [
            // "123 Main Street" or "123A Main Street"
            '/^(\d+[A-Za-z]*)\s+(.+)$/',
            // "Main Street 123" or "Main Street 123A"
            '/^(.+)\s+(\d+[A-Za-z]*)$/',
        ];

        foreach ($patterns as $pattern) {
            if (preg_match($pattern, trim($streetAddress), $matches)) {
                if (ctype_digit($matches[1][0])) {
                    // Number comes first
                    return new self($matches[2], $matches[1]);
                } else {
                    // Name comes first
                    return new self($matches[1], $matches[2]);
                }
            }
        }

        // If parsing fails, treat entire string as name with empty number
        throw new InvalidArgumentException("Cannot parse street address: {$streetAddress}");
    }

    public static function create(string $name, string $number, ?string $addition = null): self
    {
        return new self($name, $number, $addition);
    }

    public function withAddition(string $addition): self
    {
        return new self($this->name, $this->number, $addition);
    }

    public function withoutAddition(): self
    {
        return new self($this->name, $this->number);
    }

    public function format(string $style = 'default'): string
    {
        return match($style) {
            'name_first' => $this->formatNameFirst(),
            'number_first' => $this->formatNumberFirst(),
            default => $this->formatDefault()
        };
    }

    private function formatDefault(): string
    {
        $formatted = trim($this->name) . ' ' . trim($this->number);

        if ($this->addition !== null && trim($this->addition) !== '') {
            $formatted .= ' ' . trim($this->addition);
        }

        return $formatted;
    }

    private function formatNameFirst(): string
    {
        return $this->formatDefault();
    }

    private function formatNumberFirst(): string
    {
        $formatted = trim($this->number) . ' ' . trim($this->name);

        if ($this->addition !== null && trim($this->addition) !== '') {
            $formatted .= ' ' . trim($this->addition);
        }

        return $formatted;
    }

    public function getFullNumber(): string
    {
        if ($this->addition !== null && trim($this->addition) !== '') {
            return trim($this->number) . ' ' . trim($this->addition);
        }

        return trim($this->number);
    }

    public function hasAddition(): bool
    {
        return $this->addition !== null && trim($this->addition) !== '';
    }

    public function equals(self $other): bool
    {
        return $this->name === $other->name
            && $this->number === $other->number
            && $this->addition === $other->addition;
    }

    public function toString(): string
    {
        return $this->format();
    }

    public function __toString(): string
    {
        return $this->format();
    }

    public function toArray(): array
    {
        return [
            'name' => $this->name,
            'number' => $this->number,
            'addition' => $this->addition,
            'formatted' => $this->format(),
        ];
    }

    public static function fromArray(array $data): self
    {
        return new self(
            $data['name'],
            $data['number'],
            $data['addition'] ?? null
        );
    }
}
