<?php

declare(strict_types=1);

namespace App\Services;

use App\Events\Achievements\AchievementCheckRequested;
use App\Models\ActivityEvent;
use App\Models\Story;
use App\Notifications\StoryStatusNotification;

final class StoryPublicationService
{
    public function __construct(
        private readonly XPService $xp,
        private readonly ActivityService $activity,
    ) {
    }

    public function publish(Story $story, string $notificationEvent = 'published', array $attributes = []): Story
    {
        $wasPublished = $this->isPublished($story);

        $story->fill(array_merge([
            'status' => 'published',
            'published_at' => $story->published_at ?? now(),
            'scheduled_for' => null,
        ], $attributes));

        if ($story->isDirty()) {
            $story->save();
        }

        $this->afterPersistence($story, $notificationEvent, $wasPublished);

        return $story;
    }

    public function afterPersistence(Story $story, string $notificationEvent = 'published', bool $wasPublished = false): void
    {
        if (! $this->isPublished($story)) {
            return;
        }

        if (! $wasPublished && $story->creator_id !== null) {
            $this->xp->awardStoryPublished((int) $story->creator_id, (int) $story->id);
            event(new AchievementCheckRequested((int) $story->creator_id));

            try {
                $this->activity->record(
                    actorId: (int) $story->creator_id,
                    type: ActivityEvent::TYPE_UPLOAD,
                    targetType: ActivityEvent::TARGET_STORY,
                    targetId: (int) $story->id,
                    meta: [
                        'story_slug' => (string) $story->slug,
                        'story_title' => (string) $story->title,
                    ],
                );
            } catch (\Throwable) {
                // Activity logging should not block publication.
            }
        }

        $story->creator?->notify(new StoryStatusNotification($story, $notificationEvent));
    }

    private function isPublished(Story $story): bool
    {
        return $story->published_at !== null || $story->status === 'published';
    }
}