<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Requests\Artworks\ArtworkTagsStoreRequest;
use App\Http\Requests\Artworks\ArtworkTagsUpdateRequest;
use App\Models\Artwork;
use App\Models\Tag;
use App\Services\TagService;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Response;
use App\Jobs\AutoTagArtworkJob;

final class ArtworkTagController extends Controller
{
    public function __construct(
        private readonly TagService $tags,
    ) {
    }

    public function index(int $id): JsonResponse
    {
        $artwork = Artwork::query()->findOrFail($id);
        $this->authorizeOrNotFound(request()->user(), $artwork);

        $queueConnection = (string) config('queue.default', 'sync');
        $visionEnabled = (bool) config('vision.enabled', true);

        $queuedCount = 0;
        $failedCount = 0;

        if (in_array($queueConnection, ['database', 'redis'], true)) {
            try {
                $queuedCount = (int) DB::table('jobs')
                    ->where('payload', 'like', '%AutoTagArtworkJob%')
                    ->where('payload', 'like', '%' . $artwork->id . '%')
                    ->count();
            } catch (\Throwable) {
                $queuedCount = 0;
            }

            try {
                $failedCount = (int) DB::table('failed_jobs')
                    ->where('payload', 'like', '%AutoTagArtworkJob%')
                    ->where('payload', 'like', '%' . $artwork->id . '%')
                    ->count();
            } catch (\Throwable) {
                $failedCount = 0;
            }
        }

        $triggered = false;
        $shouldTrigger = request()->boolean('trigger', false);
        if ($shouldTrigger && $visionEnabled && ! empty($artwork->hash) && $queuedCount === 0) {
            AutoTagArtworkJob::dispatch((int) $artwork->id, (string) $artwork->hash);
            $triggered = true;
            $queuedCount = max(1, $queuedCount);
        }

        $tags = $artwork->tags()
            ->select('tags.id', 'tags.name', 'tags.slug')
            ->withPivot(['source', 'confidence'])
            ->orderByDesc('artwork_tag.confidence')
            ->get()
            ->map(static function ($tag): array {
                $source = (string) ($tag->pivot->source ?? 'manual');
                return [
                    'id' => (int) $tag->id,
                    'name' => (string) $tag->name,
                    'slug' => (string) $tag->slug,
                    'source' => $source,
                    'confidence' => (float) ($tag->pivot->confidence ?? 0),
                    'is_ai' => $source === 'ai',
                ];
            })
            ->values();

        return response()->json([
            'vision_enabled' => $visionEnabled,
            'tags' => $tags,
            'ai_tags' => $tags->where('is_ai', true)->values(),
            'debug' => [
                'queue_connection' => $queueConnection,
                'queued_jobs' => $queuedCount,
                'failed_jobs' => $failedCount,
                'triggered' => $triggered,
                'ai_tag_count' => (int) $tags->where('is_ai', true)->count(),
                'total_tag_count' => (int) $tags->count(),
            ],
        ]);
    }

    public function store(int $id, ArtworkTagsStoreRequest $request): JsonResponse
    {
        $artwork = Artwork::query()->findOrFail($id);
        $this->authorizeOrNotFound($request->user(), $artwork);

        try {
            $payload = $request->validated();
            $this->tags->attachUserTags($artwork, $payload['tags']);

            return response()->json(['ok' => true], Response::HTTP_CREATED);
        } catch (\Throwable $e) {
            $ref = (string) Str::uuid();
            logger()->error('Artwork tag attach failed', ['ref' => $ref, 'artwork_id' => $artwork->id, 'user_id' => $request->user()?->id, 'exception' => $e]);
            return response()->json([
                'message' => 'Unable to update tags right now.',
                'ref' => $ref,
            ], Response::HTTP_UNPROCESSABLE_ENTITY);
        }
    }

    public function update(int $id, ArtworkTagsUpdateRequest $request): JsonResponse
    {
        $artwork = Artwork::query()->findOrFail($id);
        $this->authorizeOrNotFound($request->user(), $artwork);

        try {
            $payload = $request->validated();
            $this->tags->syncTags($artwork, $payload['tags']);
            return response()->json(['ok' => true]);
        } catch (\Throwable $e) {
            $ref = (string) Str::uuid();
            logger()->error('Artwork tag sync failed', ['ref' => $ref, 'artwork_id' => $artwork->id, 'user_id' => $request->user()?->id, 'exception' => $e]);
            return response()->json([
                'message' => 'Unable to update tags right now.',
                'ref' => $ref,
            ], Response::HTTP_UNPROCESSABLE_ENTITY);
        }
    }

    public function destroy(int $id, Tag $tag): JsonResponse
    {
        $artwork = Artwork::query()->findOrFail($id);
        $this->authorizeOrNotFound(request()->user(), $artwork);

        try {
            $this->tags->detachTags($artwork, [$tag->id]);
            return response()->json(['ok' => true]);
        } catch (\Throwable $e) {
            $ref = (string) Str::uuid();
            logger()->error('Artwork tag detach failed', ['ref' => $ref, 'artwork_id' => $artwork->id, 'tag_id' => $tag->id, 'user_id' => request()->user()?->id, 'exception' => $e]);
            return response()->json([
                'message' => 'Unable to update tags right now.',
                'ref' => $ref,
            ], Response::HTTP_UNPROCESSABLE_ENTITY);
        }
    }

    private function authorizeOrNotFound($user, Artwork $artwork): void
    {
        if (! $user) {
            abort(404);
        }

        if (! $user->can('updateTags', $artwork)) {
            abort(404);
        }
    }
}
