<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Models\Artwork;
use App\Models\ArtworkDownload;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
use Symfony\Component\HttpFoundation\BinaryFileResponse;

final class ArtworkDownloadController extends Controller
{
    /**
     * Allowed original file extensions for secure server-side download.
     *
     * @var array<int, string>
     */
    private const ALLOWED_EXTENSIONS = [
        'jpg',
        'jpeg',
        'png',
        'gif',
        'webp',
        'bmp',
        'tiff',
        'zip',
        'rar',
        '7z',
        'tar',
        'gz',
    ];

    public function __invoke(Request $request, int $id): BinaryFileResponse
    {
        $artwork = Artwork::query()->find($id);
        if (! $artwork) {
            abort(404);
        }

        $filePath = $this->resolveOriginalPath($artwork);
        $ext = strtolower(ltrim((string) pathinfo($filePath, PATHINFO_EXTENSION), '.'));

        if ($filePath === '' || ! in_array($ext, self::ALLOWED_EXTENSIONS, true)) {
            abort(404);
        }

        $this->recordDownload($request, $artwork->id);
        $this->incrementDownloadCountIfAvailable($artwork->id);

        if (! File::isFile($filePath)) {
            Log::warning('Artwork original file missing for download.', [
                'artwork_id' => $artwork->id,
                'ext' => $ext,
                'resolved_path' => $filePath,
            ]);

            abort(404);
        }


        $downloadName = $this->buildDownloadFilename((string) $artwork->file_name, $ext);

        return response()->download($filePath, $downloadName);
    }

    private function resolveOriginalPath(Artwork $artwork): string
    {
        $relative = trim((string) $artwork->file_path, '/');
        $prefix = trim((string) config('uploads.object_storage.prefix', 'artworks'), '/') . '/original/';

        if ($relative !== '' && str_starts_with($relative, $prefix)) {
            $suffix = substr($relative, strlen($prefix));
            $root = rtrim((string) config('uploads.local_originals_root'), DIRECTORY_SEPARATOR);

            return $root . DIRECTORY_SEPARATOR . str_replace(['/', '\\'], DIRECTORY_SEPARATOR, (string) $suffix);
        }

        $hash = strtolower((string) $artwork->hash);
        $ext = strtolower(ltrim((string) $artwork->file_ext, '.'));
        if (! $this->isValidHash($hash) || $ext === '') {
            return '';
        }

        $root = rtrim((string) config('uploads.local_originals_root'), DIRECTORY_SEPARATOR);

        return $root
            . DIRECTORY_SEPARATOR . substr($hash, 0, 2)
            . DIRECTORY_SEPARATOR . substr($hash, 2, 2)
            . DIRECTORY_SEPARATOR . $hash . '.' . $ext;
    }

    private function recordDownload(Request $request, int $artworkId): void
    {
        try {
            $ipAddress = $request->ip();
            $ipBinary = $ipAddress ? @inet_pton($ipAddress) : false;

            ArtworkDownload::query()->create([
                'artwork_id' => $artworkId,
                'user_id' => $request->user()?->id,
                'ip' => $ipBinary !== false ? $ipBinary : null,
                'ip_address' => $ipAddress,
                'user_agent' => mb_substr((string) $request->userAgent(), 0, 1024),
                'referer' => mb_substr((string) $request->headers->get('referer'), 0, 65535),
            ]);
        } catch (\Throwable $exception) {
            Log::warning('Failed to record artwork download analytics.', [
                'artwork_id' => $artworkId,
                'error' => $exception->getMessage(),
            ]);
        }
    }

    private function incrementDownloadCountIfAvailable(int $artworkId): void
    {
        if (! Schema::hasColumn('artworks', 'download_count')) {
            return;
        }

        Artwork::query()->whereKey($artworkId)->increment('download_count');
    }

    private function isValidHash(string $hash): bool
    {
        return $hash !== '' && preg_match('/^[a-f0-9]+$/', $hash) === 1;
    }

    private function buildDownloadFilename(string $fileName, string $ext): string
    {
        $name = trim($fileName);
        $name = str_replace(['/', '\\'], '-', $name);
        $name = preg_replace('/[\x00-\x1F\x7F]/', '', $name) ?? '';
        $name = preg_replace('/\s+/', ' ', $name) ?? '';
        $name = trim((string) $name, ". \t\n\r\0\x0B");

        if ($name === '') {
            $name = 'artwork';
        }

        if (strtolower((string) pathinfo($name, PATHINFO_EXTENSION)) !== $ext) {
            $name .= '.' . $ext;
        }

        return $name;
    }
}
