<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Tag;
use App\Models\UserNegativeSignal;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Response;

final class DiscoveryNegativeSignalController extends Controller
{
    public function hideArtwork(Request $request): JsonResponse
    {
        $payload = $request->validate([
            'artwork_id' => ['required', 'integer', 'exists:artworks,id'],
            'algo_version' => ['nullable', 'string', 'max:64'],
            'source' => ['nullable', 'string', 'max:64'],
            'meta' => ['nullable', 'array'],
        ]);

        $signal = UserNegativeSignal::query()->updateOrCreate(
            [
                'user_id' => (int) $request->user()->id,
                'signal_type' => 'hide_artwork',
                'artwork_id' => (int) $payload['artwork_id'],
            ],
            [
                'tag_id' => null,
                'algo_version' => $payload['algo_version'] ?? null,
                'source' => $payload['source'] ?? 'api',
                'meta' => (array) ($payload['meta'] ?? []),
            ]
        );

        $this->recordFeedbackEvent(
            userId: (int) $request->user()->id,
            artworkId: (int) $payload['artwork_id'],
            eventType: 'hide_artwork',
            algoVersion: isset($payload['algo_version']) ? (string) $payload['algo_version'] : null,
            meta: (array) ($payload['meta'] ?? [])
        );

        return response()->json([
            'stored' => true,
            'signal_id' => (int) $signal->id,
            'signal_type' => 'hide_artwork',
        ], Response::HTTP_ACCEPTED);
    }

    public function dislikeTag(Request $request): JsonResponse
    {
        $payload = $request->validate([
            'tag_id' => ['nullable', 'integer', 'exists:tags,id'],
            'tag_slug' => ['nullable', 'string', 'max:191'],
            'artwork_id' => ['nullable', 'integer', 'exists:artworks,id'],
            'algo_version' => ['nullable', 'string', 'max:64'],
            'source' => ['nullable', 'string', 'max:64'],
            'meta' => ['nullable', 'array'],
        ]);

        $tagId = isset($payload['tag_id']) ? (int) $payload['tag_id'] : null;
        if ($tagId === null && ! empty($payload['tag_slug'])) {
            $tagId = Tag::query()->where('slug', (string) $payload['tag_slug'])->value('id');
        }

        abort_if($tagId === null || $tagId <= 0, Response::HTTP_UNPROCESSABLE_ENTITY, 'A valid tag is required.');

        $signal = UserNegativeSignal::query()->updateOrCreate(
            [
                'user_id' => (int) $request->user()->id,
                'signal_type' => 'dislike_tag',
                'tag_id' => $tagId,
            ],
            [
                'artwork_id' => null,
                'algo_version' => $payload['algo_version'] ?? null,
                'source' => $payload['source'] ?? 'api',
                'meta' => (array) ($payload['meta'] ?? []),
            ]
        );

        $this->recordFeedbackEvent(
            userId: (int) $request->user()->id,
            artworkId: isset($payload['artwork_id']) ? (int) $payload['artwork_id'] : (int) (($payload['meta']['artwork_id'] ?? 0)),
            eventType: 'dislike_tag',
            algoVersion: isset($payload['algo_version']) ? (string) $payload['algo_version'] : null,
            meta: array_merge((array) ($payload['meta'] ?? []), ['tag_id' => $tagId])
        );

        return response()->json([
            'stored' => true,
            'signal_id' => (int) $signal->id,
            'signal_type' => 'dislike_tag',
            'tag_id' => $tagId,
        ], Response::HTTP_ACCEPTED);
    }

    public function unhideArtwork(Request $request): JsonResponse
    {
        $payload = $request->validate([
            'artwork_id' => ['required', 'integer', 'exists:artworks,id'],
            'algo_version' => ['nullable', 'string', 'max:64'],
            'meta' => ['nullable', 'array'],
        ]);

        $deleted = UserNegativeSignal::query()
            ->where('user_id', (int) $request->user()->id)
            ->where('signal_type', 'hide_artwork')
            ->where('artwork_id', (int) $payload['artwork_id'])
            ->delete();

        if ($deleted > 0) {
            $this->recordFeedbackEvent(
                userId: (int) $request->user()->id,
                artworkId: (int) $payload['artwork_id'],
                eventType: 'unhide_artwork',
                algoVersion: isset($payload['algo_version']) ? (string) $payload['algo_version'] : null,
                meta: (array) ($payload['meta'] ?? [])
            );
        }

        return response()->json([
            'revoked' => $deleted > 0,
            'signal_type' => 'hide_artwork',
            'artwork_id' => (int) $payload['artwork_id'],
        ], Response::HTTP_OK);
    }

    public function undislikeTag(Request $request): JsonResponse
    {
        $payload = $request->validate([
            'tag_id' => ['nullable', 'integer', 'exists:tags,id'],
            'tag_slug' => ['nullable', 'string', 'max:191'],
            'artwork_id' => ['nullable', 'integer', 'exists:artworks,id'],
            'algo_version' => ['nullable', 'string', 'max:64'],
            'meta' => ['nullable', 'array'],
        ]);

        $tagId = isset($payload['tag_id']) ? (int) $payload['tag_id'] : null;
        if ($tagId === null && ! empty($payload['tag_slug'])) {
            $tagId = Tag::query()->where('slug', (string) $payload['tag_slug'])->value('id');
        }

        abort_if($tagId === null || $tagId <= 0, Response::HTTP_UNPROCESSABLE_ENTITY, 'A valid tag is required.');

        $deleted = UserNegativeSignal::query()
            ->where('user_id', (int) $request->user()->id)
            ->where('signal_type', 'dislike_tag')
            ->where('tag_id', $tagId)
            ->delete();

        if ($deleted > 0) {
            $this->recordFeedbackEvent(
                userId: (int) $request->user()->id,
                artworkId: isset($payload['artwork_id']) ? (int) $payload['artwork_id'] : (int) (($payload['meta']['artwork_id'] ?? 0)),
                eventType: 'undo_dislike_tag',
                algoVersion: isset($payload['algo_version']) ? (string) $payload['algo_version'] : null,
                meta: array_merge((array) ($payload['meta'] ?? []), ['tag_id' => $tagId])
            );
        }

        return response()->json([
            'revoked' => $deleted > 0,
            'signal_type' => 'dislike_tag',
            'tag_id' => $tagId,
        ], Response::HTTP_OK);
    }

    /**
     * @param array<string, mixed> $meta
     */
    private function recordFeedbackEvent(int $userId, int $artworkId, string $eventType, ?string $algoVersion = null, array $meta = []): void
    {
        if ($artworkId <= 0 || ! Schema::hasTable('user_discovery_events')) {
            return;
        }

        $categoryId = DB::table('artwork_category')
            ->where('artwork_id', $artworkId)
            ->orderBy('category_id')
            ->value('category_id');

        DB::table('user_discovery_events')->insert([
            'event_id' => (string) Str::uuid(),
            'user_id' => $userId,
            'artwork_id' => $artworkId,
            'category_id' => $categoryId !== null ? (int) $categoryId : null,
            'event_type' => $eventType,
            'event_version' => (string) config('discovery.event_version', 'event-v1'),
            'algo_version' => (string) ($algoVersion ?: config('discovery.v2.algo_version', config('discovery.algo_version', 'clip-cosine-v1'))),
            'weight' => 0.0,
            'event_date' => now()->toDateString(),
            'occurred_at' => now()->toDateTimeString(),
            'meta' => json_encode($meta, JSON_THROW_ON_ERROR),
            'created_at' => now(),
            'updated_at' => now(),
        ]);
    }
}
