<?php

declare(strict_types=1);

namespace App\Services\Studio;

use App\Models\Artwork;
use App\Models\Tag;
use App\Jobs\IndexArtworkJob;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

/**
 * Handles bulk operations on artworks for the Studio module.
 */
final class StudioBulkActionService
{
    /**
     * Execute a bulk action on the given artwork IDs, enforcing ownership.
     *
     * @param  int    $userId     The authenticated user ID
     * @param  string $action     publish|unpublish|archive|unarchive|delete|change_category|add_tags|remove_tags
     * @param  array  $artworkIds Array of artwork IDs
     * @param  array  $params     Extra params (category_id, tag_ids)
     * @return array{success: int, failed: int, errors: array}
     */
    public function execute(int $userId, string $action, array $artworkIds, array $params = []): array
    {
        $result = ['success' => 0, 'failed' => 0, 'errors' => []];

        // Validate ownership — fetch only artworks belonging to this user
        $query = Artwork::where('user_id', $userId);
        if ($action === 'unarchive') {
            $query->onlyTrashed();
        } elseif ($action === 'delete') {
            $query->withTrashed();
        }
        $artworks = $query->whereIn('id', $artworkIds)->get();

        $foundIds = $artworks->pluck('id')->all();
        $missingIds = array_diff($artworkIds, $foundIds);
        foreach ($missingIds as $id) {
            $result['failed']++;
            $result['errors'][] = "Artwork #{$id}: not found or not owned by you";
        }

        if ($artworks->isEmpty()) {
            return $result;
        }

        DB::beginTransaction();

        try {
            foreach ($artworks as $artwork) {
                $this->applyAction($artwork, $action, $params);
                $result['success']++;
            }

            DB::commit();

            // Reindex affected artworks in Meilisearch
            $this->reindexArtworks($artworks);

            Log::info('Studio bulk action completed', [
                'user_id'  => $userId,
                'action'   => $action,
                'count'    => $result['success'],
                'ids'      => $foundIds,
            ]);
        } catch (\Throwable $e) {
            DB::rollBack();
            $result['failed'] += $result['success'];
            $result['success'] = 0;
            $result['errors'][] = 'Transaction failed: ' . $e->getMessage();

            Log::error('Studio bulk action failed', [
                'user_id' => $userId,
                'action'  => $action,
                'error'   => $e->getMessage(),
            ]);
        }

        return $result;
    }

    private function applyAction(Artwork $artwork, string $action, array $params): void
    {
        match ($action) {
            'publish' => $this->publish($artwork),
            'unpublish' => $this->unpublish($artwork),
            'archive' => $artwork->delete(), // Soft delete
            'unarchive' => $artwork->restore(),
            'delete' => $artwork->forceDelete(),
            'change_category' => $this->changeCategory($artwork, $params),
            'add_tags' => $this->addTags($artwork, $params),
            'remove_tags' => $this->removeTags($artwork, $params),
            default => throw new \InvalidArgumentException("Unknown action: {$action}"),
        };
    }

    private function publish(Artwork $artwork): void
    {
        $artwork->update([
            'is_public'    => true,
            'published_at' => $artwork->published_at ?? now(),
        ]);
    }

    private function unpublish(Artwork $artwork): void
    {
        $artwork->update(['is_public' => false]);
    }

    private function changeCategory(Artwork $artwork, array $params): void
    {
        if (empty($params['category_id'])) {
            throw new \InvalidArgumentException('category_id required for change_category');
        }

        $artwork->categories()->sync([(int) $params['category_id']]);
    }

    private function addTags(Artwork $artwork, array $params): void
    {
        if (empty($params['tag_ids'])) {
            throw new \InvalidArgumentException('tag_ids required for add_tags');
        }

        $pivotData = [];
        foreach ((array) $params['tag_ids'] as $tagId) {
            $pivotData[(int) $tagId] = ['source' => 'studio_bulk', 'confidence' => 1.0];
        }

        $artwork->tags()->syncWithoutDetaching($pivotData);

        // Increment usage counts
        Tag::whereIn('id', array_keys($pivotData))
            ->increment('usage_count');
    }

    private function removeTags(Artwork $artwork, array $params): void
    {
        if (empty($params['tag_ids'])) {
            throw new \InvalidArgumentException('tag_ids required for remove_tags');
        }

        $tagIds = array_map('intval', (array) $params['tag_ids']);
        $artwork->tags()->detach($tagIds);

        Tag::whereIn('id', $tagIds)
            ->where('usage_count', '>', 0)
            ->decrement('usage_count');
    }

    /**
     * Trigger Meilisearch reindex for the given artworks.
     */
    private function reindexArtworks(\Illuminate\Database\Eloquent\Collection $artworks): void
    {
        foreach ($artworks as $artwork) {
            IndexArtworkJob::dispatch((int) $artwork->id);
        }
    }
}
