<?php

declare(strict_types=1);

namespace App\Services\NovaCards;

use App\Jobs\UpdateNovaCardStatsJob;
use App\Models\NovaCard;
use App\Models\NovaCardCollection;
use App\Models\NovaCardCollectionItem;
use App\Models\User;
use Illuminate\Support\Str;

class NovaCardCollectionService
{
    public function createCollection(User $user, array $attributes): NovaCardCollection
    {
        $name = trim((string) ($attributes['name'] ?? 'Saved Cards'));
        $slug = $this->uniqueSlug($user, Str::slug($attributes['slug'] ?? $name) ?: 'saved-cards');

        return NovaCardCollection::query()->create([
            'user_id' => $user->id,
            'slug' => $slug,
            'name' => $name,
            'description' => $attributes['description'] ?? null,
            'visibility' => $attributes['visibility'] ?? NovaCardCollection::VISIBILITY_PRIVATE,
            'official' => false,
        ]);
    }

    public function createManagedCollection(array $attributes): NovaCardCollection
    {
        $owner = User::query()->findOrFail((int) $attributes['user_id']);
        $name = trim((string) ($attributes['name'] ?? 'Untitled Collection'));
        $slug = $this->uniqueSlug($owner, Str::slug($attributes['slug'] ?? $name) ?: 'nova-collection');

        return NovaCardCollection::query()->create([
            'user_id' => $owner->id,
            'slug' => $slug,
            'name' => $name,
            'description' => $attributes['description'] ?? null,
            'visibility' => $attributes['visibility'] ?? NovaCardCollection::VISIBILITY_PUBLIC,
            'official' => (bool) ($attributes['official'] ?? false),
            'featured' => (bool) ($attributes['featured'] ?? false),
        ]);
    }

    public function listCollections(User $user): array
    {
        return NovaCardCollection::query()
            ->withCount('items')
            ->where('user_id', $user->id)
            ->orderByDesc('updated_at')
            ->get()
            ->map(fn (NovaCardCollection $collection): array => [
                'id' => (int) $collection->id,
                'slug' => (string) $collection->slug,
                'name' => (string) $collection->name,
                'description' => $collection->description,
                'visibility' => (string) $collection->visibility,
                'featured' => (bool) $collection->featured,
                'cards_count' => (int) $collection->cards_count,
                'items_count' => (int) $collection->items_count,
            ])
            ->values()
            ->all();
    }

    public function saveCard(User $user, NovaCard $card, ?int $collectionId = null, ?string $note = null): NovaCardCollection
    {
        $collection = $collectionId
            ? NovaCardCollection::query()->where('user_id', $user->id)->findOrFail($collectionId)
            : $this->defaultCollection($user);

        $this->addCardToCollection($collection, $card, $note);

        return $collection->refresh();
    }

    public function addCardToCollection(NovaCardCollection $collection, NovaCard $card, ?string $note = null, ?int $sortOrder = null): NovaCardCollectionItem
    {
        $sortOrder ??= (int) NovaCardCollectionItem::query()->where('collection_id', $collection->id)->max('sort_order') + 1;

        $item = NovaCardCollectionItem::query()->updateOrCreate([
            'collection_id' => $collection->id,
            'card_id' => $card->id,
        ], [
            'note' => $note,
            'sort_order' => $sortOrder,
        ]);

        $this->refreshCounts($collection, $card);

        return $item;
    }

    public function unsaveCard(User $user, NovaCard $card, ?int $collectionId = null): void
    {
        $query = NovaCardCollectionItem::query()
            ->where('card_id', $card->id)
            ->whereHas('collection', fn ($builder) => $builder->where('user_id', $user->id));

        if ($collectionId !== null) {
            $query->where('collection_id', $collectionId);
        }

        $collectionIds = $query->pluck('collection_id')->unique()->all();
        $query->delete();

        foreach ($collectionIds as $id) {
            $collection = NovaCardCollection::query()->find($id);
            if ($collection) {
                $this->refreshCounts($collection, $card);
            }
        }
    }

    public function removeCardFromCollection(NovaCardCollection $collection, NovaCard $card): void
    {
        NovaCardCollectionItem::query()
            ->where('collection_id', $collection->id)
            ->where('card_id', $card->id)
            ->delete();

        $this->refreshCounts($collection, $card);
    }

    public function defaultCollection(User $user): NovaCardCollection
    {
        return NovaCardCollection::query()->firstOrCreate([
            'user_id' => $user->id,
            'slug' => 'saved-cards',
        ], [
            'name' => 'Saved Cards',
            'description' => 'Private library of Nova Cards saved for remixing, referencing, and future collections.',
            'visibility' => NovaCardCollection::VISIBILITY_PRIVATE,
            'official' => false,
        ]);
    }

    private function refreshCounts(NovaCardCollection $collection, NovaCard $card): void
    {
        $collection->forceFill([
            'cards_count' => NovaCardCollectionItem::query()->where('collection_id', $collection->id)->count(),
        ])->save();

        UpdateNovaCardStatsJob::dispatch($card->id);
    }

    private function uniqueSlug(User $user, string $base): string
    {
        $slug = $base;
        $suffix = 2;

        while (NovaCardCollection::query()->where('user_id', $user->id)->where('slug', $slug)->exists()) {
            $slug = $base . '-' . $suffix;
            $suffix++;
        }

        return $slug;
    }
}