<?php

declare(strict_types=1);

namespace App\Services;

use App\Models\Collection;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class CollectionSaveService
{
    public function save(User $actor, Collection $collection, ?string $context = null, array $contextMeta = []): bool
    {
        $this->guard($actor, $collection);

        $inserted = false;

        DB::transaction(function () use ($actor, $collection, $context, $contextMeta, &$inserted): void {
            $rows = DB::table('collection_saves')->insertOrIgnore([
                'collection_id' => $collection->id,
                'user_id' => $actor->id,
                'created_at' => now(),
                'last_viewed_at' => now(),
                'save_context' => $context,
                'save_context_meta_json' => $contextMeta === [] ? null : json_encode($contextMeta, JSON_THROW_ON_ERROR),
            ]);

            if ($rows === 0) {
                DB::table('collection_saves')
                    ->where('collection_id', $collection->id)
                    ->where('user_id', $actor->id)
                    ->update([
                        'last_viewed_at' => now(),
                        'save_context' => $context,
                        'save_context_meta_json' => $contextMeta === [] ? null : json_encode($contextMeta, JSON_THROW_ON_ERROR),
                    ]);

                return;
            }

            $inserted = true;

            DB::table('collections')
                ->where('id', $collection->id)
                ->update([
                    'saves_count' => DB::raw('saves_count + 1'),
                    'last_activity_at' => now(),
                    'updated_at' => now(),
                ]);
        });

        return $inserted;
    }

    public function touchSavedCollectionView(?User $actor, Collection $collection): void
    {
        if (! $actor) {
            return;
        }

        DB::table('collection_saves')
            ->where('collection_id', $collection->id)
            ->where('user_id', $actor->id)
            ->update([
                'last_viewed_at' => now(),
            ]);
    }

    public function unsave(User $actor, Collection $collection): bool
    {
        $deleted = false;

        DB::transaction(function () use ($actor, $collection, &$deleted): void {
            $rows = DB::table('collection_saves')
                ->where('collection_id', $collection->id)
                ->where('user_id', $actor->id)
                ->delete();

            if ($rows === 0) {
                return;
            }

            $deleted = true;

            $savedListIds = DB::table('collection_saved_lists')
                ->where('user_id', $actor->id)
                ->pluck('id');

            if ($savedListIds->isNotEmpty()) {
                DB::table('collection_saved_list_items')
                    ->whereIn('saved_list_id', $savedListIds->all())
                    ->where('collection_id', $collection->id)
                    ->delete();
            }

            DB::table('collections')
                ->where('id', $collection->id)
                ->where('saves_count', '>', 0)
                ->update([
                    'saves_count' => DB::raw('saves_count - 1'),
                    'updated_at' => now(),
                ]);
        });

        return $deleted;
    }

    public function isSaved(?User $viewer, Collection $collection): bool
    {
        if (! $viewer) {
            return false;
        }

        return DB::table('collection_saves')
            ->where('collection_id', $collection->id)
            ->where('user_id', $viewer->id)
            ->exists();
    }

    private function guard(User $actor, Collection $collection): void
    {
        if (! $collection->canBeSavedBy($actor)) {
            throw ValidationException::withMessages([
                'collection' => 'This collection cannot be saved.',
            ]);
        }
    }
}
