<?php

declare(strict_types=1);

namespace App\Models;

use App\Domain\Blog\ContentFormat;
use App\Domain\Blog\ContentRenderer;
use App\Domain\Media\ArticleCoverService;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Article extends Model
{
    protected $fillable = [
        'category_id',
        'user_id',
        'title',
        'content',
        'content_format',
        'description',
        'keywords',
        'published_at',
        'views',
        'comments_count',
        'slug',
        'stick',
        'visible',
        'close_comment',
        'read_password',
        'ai_summary',
        'ai_suggestions',
        'ai_polished_content',
        'legacy_attachments',
        'cover_disk',
        'cover_path',
        'cover_source',
        'cover_status',
        'cover_generated_at',
    ];

    protected function casts(): array
    {
        return [
            'published_at' => 'datetime',
            'views' => 'integer',
            'comments_count' => 'integer',
            'stick' => 'boolean',
            'visible' => 'boolean',
            'close_comment' => 'boolean',
            'ai_suggestions' => 'array',
            'legacy_attachments' => 'array',
            'cover_generated_at' => 'datetime',
        ];
    }

    public function hasCover(): bool
    {
        return $this->cover_status === 'ready' && filled($this->cover_path);
    }

    public function coverUrl(): ?string
    {
        return app(ArticleCoverService::class)->publicUrl($this);
    }

    protected function contentFormat(): Attribute
    {
        return Attribute::make(
            get: fn (?string $value): string => ContentFormat::normalize($value, ContentFormat::HTML),
            set: fn (?string $value): string => ContentFormat::normalize($value, ContentFormat::HTML),
        );
    }

    public function renderedHtml(): string
    {
        return $this->rendered()['html'];
    }

    /**
     * @return array{html: string, toc: list<array{level: int, id: string, text: string}>}
     */
    public function rendered(): array
    {
        return app(ContentRenderer::class)->render(
            (string) $this->content,
            $this->content_format,
            $this->id,
        );
    }

    public function isMarkdown(): bool
    {
        return $this->content_format === ContentFormat::MARKDOWN;
    }

    public function category(): BelongsTo
    {
        return $this->belongsTo(Category::class);
    }

    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    public function comments(): HasMany
    {
        return $this->hasMany(Comment::class);
    }

    public function tags(): BelongsToMany
    {
        return $this->belongsToMany(Tag::class);
    }

    public function attachments(): HasMany
    {
        return $this->hasMany(Attachment::class);
    }

    public function scopeVisible(Builder $query): Builder
    {
        return $query->where('visible', true);
    }

    public function scopePublished(Builder $query): Builder
    {
        return $query
            ->whereNotNull('published_at')
            ->where('published_at', '<=', now());
    }
}
