<?php

declare(strict_types=1);

namespace App\Framework\Core\ValueObjects\StructuredData;

use App\Framework\Http\Url\Url;

/**
 * Article Structured Data
 *
 * Schema.org Article type for blog posts, news articles, etc.
 * Framework-compliant readonly value object with type enum support.
 *
 * @see https://schema.org/Article
 * @psalm-immutable
 */
final readonly class Article implements StructuredData
{
    /**
     * @param string $headline Article headline (max 110 characters recommended)
     * @param Url $url Article URL
     * @param \DateTimeImmutable $datePublished Publication date
     * @param ArticleType $type Article type variant
     * @param Person|null $author Article author
     * @param Organization|null $publisher Publisher organization
     * @param string|null $description Article description/excerpt
     * @param \DateTimeImmutable|null $dateModified Last modification date
     * @param Url|null $image Main article image URL
     * @param array<string> $keywords Article keywords
     */
    public function __construct(
        public string $headline,
        public Url $url,
        public \DateTimeImmutable $datePublished,
        public ArticleType $type = ArticleType::ARTICLE,
        public ?Person $author = null,
        public ?Organization $publisher = null,
        public ?string $description = null,
        public ?\DateTimeImmutable $dateModified = null,
        public ?Url $image = null,
        public array $keywords = []
    ) {
        if (mb_strlen($headline) > 110) {
            throw new \InvalidArgumentException('Article headline should not exceed 110 characters for optimal SEO');
        }
    }

    public function toJsonLd(): array
    {
        $data = [
            '@context' => 'https://schema.org',
            '@type' => $this->type->value,
            'headline' => $this->headline,
            'url' => $this->url->toString(),
            'datePublished' => $this->datePublished->format('c'),
        ];

        if ($this->author !== null) {
            $data['author'] = $this->author->toJsonLd();
        }

        if ($this->publisher !== null) {
            $data['publisher'] = $this->publisher->toJsonLd();
        }

        if ($this->description !== null) {
            $data['description'] = $this->description;
        }

        if ($this->dateModified !== null) {
            $data['dateModified'] = $this->dateModified->format('c');
        }

        if ($this->image !== null) {
            $data['image'] = $this->image->toString();
        }

        if (!empty($this->keywords)) {
            $data['keywords'] = implode(', ', $this->keywords);
        }

        return $data;
    }

    public function toScript(): string
    {
        $json = json_encode($this->toJsonLd(), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);

        return '<script type="application/ld+json">' . $json . '</script>';
    }

    /**
     * With author (immutable)
     */
    public function withAuthor(?Person $author): self
    {
        return new self(
            headline: $this->headline,
            url: $this->url,
            datePublished: $this->datePublished,
            type: $this->type,
            author: $author,
            publisher: $this->publisher,
            description: $this->description,
            dateModified: $this->dateModified,
            image: $this->image,
            keywords: $this->keywords
        );
    }

    /**
     * With publisher (immutable)
     */
    public function withPublisher(?Organization $publisher): self
    {
        return new self(
            headline: $this->headline,
            url: $this->url,
            datePublished: $this->datePublished,
            type: $this->type,
            author: $this->author,
            publisher: $publisher,
            description: $this->description,
            dateModified: $this->dateModified,
            image: $this->image,
            keywords: $this->keywords
        );
    }
}
