<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Artwork;
use App\Services\ArtworkStatsService;
use App\Services\ThumbnailPresenter;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

/**
 * POST /api/art/{id}/download
 *
 * Records a download event and returns the full-resolution download URL.
 *
 * Responsibilities:
 *   1. Validates the artwork is public and published.
 *   2. Inserts a row in artwork_downloads (artwork_id, user_id, ip, user_agent).
 *   3. Increments artwork_stats.downloads + forwards to creator stats.
 *   4. Returns {"ok": true, "url": "<download_url>"} so the frontend can
 *      trigger the actual browser download.
 *
 * The frontend fires this POST on click, then uses the returned URL to
 * trigger the file download (or falls back to the pre-resolved URL it
 * already has).
 */
final class ArtworkDownloadController extends Controller
{
    public function __construct(private readonly ArtworkStatsService $stats) {}

    public function __invoke(Request $request, int $id): JsonResponse
    {
        $artwork = Artwork::public()
            ->published()
            ->with(['user:id'])
            ->where('id', $id)
            ->first();

        if (! $artwork) {
            return response()->json(['error' => 'Not found'], 404);
        }

        // Record the download event — non-blocking, errors are swallowed.
        $this->recordDownload($request, $artwork);

        // Increment counters — deferred via Redis when available.
        try {
            $this->stats->incrementDownloads((int) $artwork->id, 1, defer: true);
        } catch (\Throwable) {
            // Stats failure must never interrupt the download.
        }

        // Resolve the highest-resolution download URL available.
        $url = $this->resolveDownloadUrl($artwork);

        // Build a user-friendly download filename: "title-slug.file_ext"
        $ext      = $artwork->file_ext ?: $artwork->thumb_ext ?: 'webp';
        $slug     = Str::slug((string) ($artwork->slug ?: $artwork->title)) ?: (string) $artwork->id;
        $filename = $slug . '.' . $ext;

        return response()->json([
            'ok'       => true,
            'url'      => $url,
            'filename' => $filename,
            'size'     => (int) ($artwork->file_size ?? 0),
        ]);
    }

    /**
     * Insert a row in artwork_downloads.
     * Uses a raw insert for the binary(16) IP column.
     * Silently ignores failures (analytics should never break user flow).
     */
    private function recordDownload(Request $request, Artwork $artwork): void
    {
        try {
            $ip  = $request->ip() ?? '0.0.0.0';
            $bin = @inet_pton($ip);

            DB::table('artwork_downloads')->insert([
                'artwork_id' => $artwork->id,
                'user_id'    => $request->user()?->id,
                'ip'         => $bin !== false ? $bin : null,
                'ip_address' => mb_substr((string) $ip, 0, 45),
                'user_agent' => mb_substr((string) $request->userAgent(), 0, 1024),
                'referer'    => mb_substr((string) $request->headers->get('referer'), 0, 65535),
                'created_at' => now(),
            ]);
        } catch (\Throwable) {
            // Analytics failure must never interrupt the download.
        }
    }

    /**
     * Resolve the original full-resolution CDN URL.
     *
     * Originals are stored at: {cdn}/original/{h1}/{h2}/{hash}.{file_ext}
     * h1 = first 2 chars of hash, h2 = next 2 chars, filename = full hash + file_ext.
     * Falls back to XL → LG → MD thumbnail when hash is unavailable.
     */
    private function resolveDownloadUrl(Artwork $artwork): string
    {
        $filePath = trim((string) ($artwork->file_path ?? ''), '/');
        $cdn = rtrim((string) config('cdn.files_url', 'https://files.skinbase.org'), '/');

        if ($filePath !== '') {
            return $cdn . '/' . $filePath;
        }

        $hash = $artwork->hash ?? null;
        $ext  = ltrim((string) ($artwork->file_ext ?: $artwork->thumb_ext ?: 'webp'), '.');

        if (!empty($hash)) {
            $h   = strtolower(preg_replace('/[^a-f0-9]/', '', $hash));
            $h1  = substr($h, 0, 2);
            $h2  = substr($h, 2, 2);
            $prefix = trim((string) config('uploads.object_storage.prefix', 'artworks'), '/');

            return sprintf('%s/%s/original/%s/%s/%s.%s', $cdn, $prefix, $h1, $h2, $h, $ext);
        }

        // Fallback: best available thumbnail size
        foreach (['xl', 'lg', 'md'] as $size) {
            $thumb = ThumbnailPresenter::present($artwork, $size);
            if (!empty($thumb['url'])) {
                return (string) $thumb['url'];
            }
        }

        return '';
    }
}
