<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Artwork;
use App\Services\TagNormalizer;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

/**
 * Synchronous Vision tag suggestions for the upload wizard.
 *
 * POST /api/uploads/{id}/vision-suggest
 *
 * Calls the Vision gateway (/analyze/all) synchronously and returns
 * normalised tag suggestions immediately — without going through the queue.
 * The queue-based AutoTagArtworkJob still runs in the background and writes
 * to the DB; this endpoint gives the user instant pre-fill on Step 2.
 */
final class UploadVisionSuggestController extends Controller
{
    public function __construct(
        private readonly TagNormalizer $normalizer,
    ) {}

    public function __invoke(int $id, Request $request): JsonResponse
    {
        if (! (bool) config('vision.enabled', true)) {
            return response()->json(['tags' => [], 'vision_enabled' => false]);
        }

        $artwork = Artwork::query()->findOrFail($id);
        $this->authorizeOrNotFound($request->user(), $artwork);

        $imageUrl = $this->buildImageUrl((string) $artwork->hash);
        if ($imageUrl === null) {
            return response()->json([
                'tags' => [],
                'vision_enabled' => true,
                'reason' => 'image_url_unavailable',
            ]);
        }

        $gatewayBase = trim((string) config('vision.gateway.base_url', config('vision.clip.base_url', '')));
        if ($gatewayBase === '') {
            return response()->json([
                'tags' => [],
                'vision_enabled' => true,
                'reason' => 'gateway_not_configured',
            ]);
        }

        $url      = rtrim($gatewayBase, '/') . '/analyze/all';
        $limit    = min(20, max(5, (int) ($request->query('limit', 10))));
        $timeout  = (int) config('vision.gateway.timeout_seconds', 10);
        $cTimeout = (int) config('vision.gateway.connect_timeout_seconds', 3);
        $ref      = (string) Str::uuid();

        try {
            /** @var \Illuminate\Http\Client\Response $response */
            $response = Http::acceptJson()
                ->connectTimeout(max(1, $cTimeout))
                ->timeout(max(1, $timeout))
                ->withHeaders(['X-Request-ID' => $ref])
                ->post($url, [
                    'url'   => $imageUrl,
                    'limit' => $limit,
                ]);

            if (! $response->ok()) {
                Log::warning('vision-suggest: non-ok response', [
                    'ref'        => $ref,
                    'artwork_id' => $id,
                    'status'     => $response->status(),
                    'body'       => Str::limit($response->body(), 400),
                ]);

                return response()->json([
                    'tags'           => [],
                    'vision_enabled' => true,
                    'reason'         => 'gateway_error_' . $response->status(),
                ]);
            }

            $tags = $this->parseGatewayResponse($response->json());

            return response()->json([
                'tags'           => $tags,
                'vision_enabled' => true,
                'source'         => 'gateway_sync',
            ]);

        } catch (\Throwable $e) {
            Log::warning('vision-suggest: request failed', [
                'ref'        => $ref,
                'artwork_id' => $id,
                'error'      => $e->getMessage(),
            ]);

            return response()->json([
                'tags'           => [],
                'vision_enabled' => true,
                'reason'         => 'gateway_exception',
            ]);
        }
    }

    // ── helpers ──────────────────────────────────────────────────────────────

    private function buildImageUrl(string $hash): ?string
    {
        $base = rtrim((string) config('cdn.files_url', ''), '/');
        if ($base === '') {
            return null;
        }

        $clean = strtolower((string) preg_replace('/[^a-z0-9]/', '', $hash));
        $clean = str_pad($clean, 6, '0');
        $seg   = [substr($clean, 0, 2) ?: '00', substr($clean, 2, 2) ?: '00', substr($clean, 4, 2) ?: '00'];
        $variant = (string) config('vision.image_variant', 'md');

        return $base . '/img/' . implode('/', $seg) . '/' . $variant . '.webp';
    }

    /**
     * Parse the /analyze/all gateway response.
     *
     * The gateway returns a unified object:
     *   { clip: [{tag, confidence}], blip: ["caption1"], yolo: [{tag, confidence}] }
     * or a flat list of tags directly.
     *
     * @param  mixed $json
     * @return array<int, array{name: string, slug: string, confidence: float|null, source: string, is_ai: true}>
     */
    private function parseGatewayResponse(mixed $json): array
    {
        $raw = [];

        if (! is_array($json)) {
            return [];
        }

        // Unified gateway response
        if (isset($json['clip']) && is_array($json['clip'])) {
            foreach ($json['clip'] as $item) {
                $raw[] = ['tag' => $item['tag'] ?? $item['name'] ?? '', 'confidence' => $item['confidence'] ?? null, 'source' => 'clip'];
            }
        }

        if (isset($json['yolo']) && is_array($json['yolo'])) {
            foreach ($json['yolo'] as $item) {
                $raw[] = ['tag' => $item['tag'] ?? $item['label'] ?? $item['name'] ?? '', 'confidence' => $item['confidence'] ?? null, 'source' => 'yolo'];
            }
        }

        // Flat lists
        if ($raw === []) {
            $list = $json['tags'] ?? $json['data'] ?? $json;
            if (is_array($list)) {
                foreach ($list as $item) {
                    if (is_array($item)) {
                        $raw[] = ['tag' => $item['tag'] ?? $item['name'] ?? $item['label'] ?? '', 'confidence' => $item['confidence'] ?? null, 'source' => 'vision'];
                    } elseif (is_string($item)) {
                        $raw[] = ['tag' => $item, 'confidence' => null, 'source' => 'vision'];
                    }
                }
            }
        }

        // Deduplicate by slug, keep highest confidence
        $bySlug = [];
        foreach ($raw as $r) {
            $slug = $this->normalizer->normalize((string) ($r['tag'] ?? ''));
            if ($slug === '') {
                continue;
            }
            $conf = isset($r['confidence']) && is_numeric($r['confidence']) ? (float) $r['confidence'] : null;

            if (! isset($bySlug[$slug]) || ($conf !== null && $conf > (float) ($bySlug[$slug]['confidence'] ?? 0))) {
                $bySlug[$slug] = [
                    'name'       => ucwords(str_replace(['-', '_'], ' ', $slug)),
                    'slug'       => $slug,
                    'confidence' => $conf,
                    'source'     => $r['source'] ?? 'vision',
                    'is_ai'      => true,
                ];
            }
        }

        // Sort by confidence desc
        $sorted = array_values($bySlug);
        usort($sorted, static fn ($a, $b) => ($b['confidence'] ?? 0) <=> ($a['confidence'] ?? 0));

        return $sorted;
    }

    private function authorizeOrNotFound(mixed $user, Artwork $artwork): void
    {
        if (! $user) {
            abort(404);
        }
        if ((int) $artwork->user_id !== (int) $user->id) {
            abort(404);
        }
    }
}
