<?php

declare(strict_types=1);

namespace App\Services;

use App\Models\Collection;
use App\Models\User;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class CollectionLinkedCollectionsService
{
    public function linkedCollections(Collection $collection): EloquentCollection
    {
        return $collection->manualRelatedCollections()
            ->with(['user:id,username,name', 'coverArtwork:id,user_id,title,slug,hash,thumb_ext,published_at,is_public,is_approved,deleted_at'])
            ->get();
    }

    public function publicLinkedCollections(Collection $collection, int $limit = 6): EloquentCollection
    {
        return $collection->manualRelatedCollections()
            ->public()
            ->with(['user:id,username,name', 'coverArtwork:id,user_id,title,slug,hash,thumb_ext,published_at,is_public,is_approved,deleted_at'])
            ->limit(max(1, min($limit, 12)))
            ->get();
    }

    public function manageableLinkOptions(Collection $collection, User $actor, int $limit = 24): EloquentCollection
    {
        $linkedIds = $collection->manualRelatedCollections()->pluck('collections.id')->map(static fn ($id) => (int) $id)->all();

        return Collection::query()
            ->with([
                'user:id,username,name',
                'members',
                'coverArtwork:id,user_id,title,slug,hash,thumb_ext,published_at,is_public,is_approved,deleted_at',
            ])
            ->where('id', '!=', $collection->id)
            ->whereNotIn('id', $linkedIds)
            ->orderByDesc('updated_at')
            ->get()
            ->filter(fn (Collection $candidate): bool => $candidate->canBeManagedBy($actor))
            ->take(max(1, min($limit, 48)))
            ->values();
    }

    /**
     * @param array<int, int|string> $relatedCollectionIds
     */
    public function syncLinks(Collection $collection, User $actor, array $relatedCollectionIds): Collection
    {
        $normalizedIds = collect($relatedCollectionIds)
            ->map(static fn ($id) => (int) $id)
            ->filter(static fn (int $id): bool => $id > 0)
            ->reject(fn (int $id): bool => $id === (int) $collection->id)
            ->unique()
            ->values();

        $targets = $normalizedIds->isEmpty()
            ? collect()
            : Collection::query()
                ->with('members')
                ->whereIn('id', $normalizedIds->all())
                ->get();

        if ($targets->count() !== $normalizedIds->count()) {
            throw ValidationException::withMessages([
                'related_collection_ids' => 'Choose valid collections to link.',
            ]);
        }

        if ($targets->contains(fn (Collection $target): bool => ! $target->canBeManagedBy($actor))) {
            throw ValidationException::withMessages([
                'related_collection_ids' => 'You can only link collections that you can manage.',
            ]);
        }

        $before = $collection->manualRelatedCollections()->pluck('collections.id')->map(static fn ($id) => (int) $id)->all();

        DB::transaction(function () use ($collection, $actor, $normalizedIds): void {
            DB::table('collection_related_links')
                ->where('collection_id', $collection->id)
                ->delete();

            if ($normalizedIds->isEmpty()) {
                return;
            }

            $now = now();

            DB::table('collection_related_links')->insert($normalizedIds->values()->map(
                fn (int $relatedId, int $index): array => [
                    'collection_id' => $collection->id,
                    'related_collection_id' => $relatedId,
                    'sort_order' => $index,
                    'created_by_user_id' => $actor->id,
                    'created_at' => $now,
                    'updated_at' => $now,
                ]
            )->all());
        });

        $fresh = $collection->fresh(['user.profile', 'coverArtwork']);

        app(CollectionHistoryService::class)->record($fresh, $actor, 'linked_collections_updated', 'Manual linked collections updated.', [
            'related_collection_ids' => $before,
        ], [
            'related_collection_ids' => $normalizedIds->all(),
        ]);

        return $fresh;
    }
}