<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Models\Artwork;
use App\Models\EnhanceJob;
use App\Services\Enhance\EnhanceService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Inertia\Inertia;
use Inertia\Response;

final class EnhanceController extends Controller
{
    public function __construct(
        private readonly EnhanceService $enhanceService,
    ) {
    }

    public function index(Request $request): Response
    {
        $this->authorize('viewAny', EnhanceJob::class);

        $jobs = EnhanceJob::query()
            ->where('user_id', (int) $request->user()->id)
            ->with('artwork:id,title,slug')
            ->latest('id')
            ->paginate(12)
            ->withQueryString()
            ->through(fn (EnhanceJob $job): array => $this->serializeJobListItem($job));

        $latestCompleted = EnhanceJob::query()
            ->where('user_id', (int) $request->user()->id)
            ->where('status', EnhanceJob::STATUS_COMPLETED)
            ->latest('finished_at')
            ->limit(4)
            ->get()
            ->map(fn (EnhanceJob $job): array => $this->serializeJobListItem($job))
            ->all();

        return Inertia::render('Enhance/Index', [
            'title' => 'Skinbase Enhance',
            'jobs' => $jobs,
            'latestCompleted' => $latestCompleted,
            'createUrl' => route('enhance.create'),
            'indexUrl' => route('enhance.index'),
            'dailyLimit' => (int) config('enhance.daily_limit', 10),
            'enhanceConfig' => $this->enhanceService->frontendConfig(),
        ]);
    }

    public function create(Request $request): Response
    {
        $this->authorize('create', EnhanceJob::class);

        $selectedArtwork = null;

        if (($artworkId = (int) $request->integer('artwork')) > 0) {
            $artwork = Artwork::query()
                ->select(['id', 'user_id', 'title', 'slug'])
                ->findOrFail($artworkId);

            $actor = $request->user();
            abort_unless($actor !== null, 403);

            $isOwner = (int) $artwork->user_id === (int) $actor->id;
            $isStaff = $actor->isAdmin() || $actor->isModerator();

            abort_unless($isOwner || $isStaff, 403);

            $selectedArtwork = [
                'id' => $artwork->id,
                'title' => $artwork->title,
                'show_url' => route('art.show', ['id' => $artwork->id, 'slug' => $artwork->slug]),
                'store_url' => route('artworks.enhance.store', ['artwork' => $artwork->id]),
            ];
        }

        return Inertia::render('Enhance/Create', [
            'title' => 'Skinbase Enhance',
            'options' => $this->optionsPayload(),
            'storeUrl' => route('enhance.store'),
            'indexUrl' => route('enhance.index'),
            'maxUploadMb' => (int) config('enhance.max_upload_mb', 20),
            'selectedArtwork' => $selectedArtwork,
            'enhanceConfig' => $this->enhanceService->frontendConfig(),
        ]);
    }

    public function store(Request $request): RedirectResponse
    {
        $this->authorize('create', EnhanceJob::class);

        $validated = $request->validate([
            'image' => ['required', 'file', 'mimetypes:image/jpeg,image/png,image/webp', 'max:' . ((int) config('enhance.max_upload_mb', 20) * 1024)],
            'scale' => ['required', 'integer', Rule::in((array) config('enhance.allowed_scales', [2, 4]))],
            'mode' => ['required', 'string', Rule::in((array) config('enhance.allowed_modes', ['standard', 'artwork', 'photo', 'illustration']))],
        ]);

        $job = $this->enhanceService->createFromUpload($request->user(), $request->file('image'), $validated);

        return redirect()
            ->route('enhance.show', ['enhanceJob' => $job])
            ->with('success', 'Enhance job created.');
    }

    public function show(EnhanceJob $enhanceJob): Response
    {
        $this->authorize('view', $enhanceJob);
        $enhanceJob->loadMissing('artwork:id,title,slug');

        return Inertia::render('Enhance/Show', [
            'title' => 'Enhance Job',
            'job' => $this->serializeJobDetail($enhanceJob),
            'indexUrl' => route('enhance.index'),
            'createUrl' => route('enhance.create'),
            'enhanceConfig' => $this->enhanceService->frontendConfig(),
        ]);
    }

    public function retry(EnhanceJob $enhanceJob): RedirectResponse
    {
        $this->authorize('retry', $enhanceJob);

        $job = $this->enhanceService->retry($enhanceJob);

        return redirect()
            ->route('enhance.show', ['enhanceJob' => $job])
            ->with('success', 'Enhance job queued again.');
    }

    public function destroy(EnhanceJob $enhanceJob): RedirectResponse
    {
        $this->authorize('delete', $enhanceJob);

        $this->enhanceService->delete($enhanceJob);

        return redirect()
            ->route('enhance.index')
            ->with('success', 'Enhance job deleted.');
    }

    private function optionsPayload(): array
    {
        return [
            'modes' => array_map(fn (string $mode): array => [
                'value' => $mode,
                'label' => ucfirst($mode),
            ], (array) config('enhance.allowed_modes', [])),
            'scales' => array_map(fn (int $scale): array => [
                'value' => $scale,
                'label' => $scale . 'x',
            ], array_map('intval', (array) config('enhance.allowed_scales', []))),
        ];
    }

    private function serializeJobListItem(EnhanceJob $job): array
    {
        return [
            'id' => $job->id,
            'status' => (string) $job->status,
            'engine' => (string) $job->engine,
            'mode' => (string) $job->mode,
            'scale' => (int) $job->scale,
            'source_url' => $job->sourceUrl(),
            'output_url' => $job->outputUrl(),
            'preview_url' => $job->previewUrl(),
            'input_width' => (int) ($job->input_width ?? 0),
            'input_height' => (int) ($job->input_height ?? 0),
            'output_width' => (int) ($job->output_width ?? 0),
            'output_height' => (int) ($job->output_height ?? 0),
            'error_message' => $job->error_message,
            'processing_seconds' => $job->processing_seconds,
            'created_at' => optional($job->created_at)?->toIso8601String(),
            'finished_at' => optional($job->finished_at)?->toIso8601String(),
            'show_url' => route('enhance.show', ['enhanceJob' => $job]),
            'artwork' => $job->artwork ? [
                'id' => $job->artwork->id,
                'title' => $job->artwork->title,
                'slug' => $job->artwork->slug,
                'url' => route('art.show', ['id' => $job->artwork->id, 'slug' => $job->artwork->slug]),
            ] : null,
        ];
    }

    private function serializeJobDetail(EnhanceJob $job): array
    {
        return $this->serializeJobListItem($job) + [
            'input_filesize' => (int) ($job->input_filesize ?? 0),
            'input_mime' => $job->input_mime,
            'output_filesize' => (int) ($job->output_filesize ?? 0),
            'output_mime' => $job->output_mime,
            'metadata' => $job->metadata ?? [],
            'queued_at' => optional($job->queued_at)?->toIso8601String(),
            'started_at' => optional($job->started_at)?->toIso8601String(),
            'deleted_at' => optional($job->deleted_at)?->toIso8601String(),
            'expires_at' => optional($job->expires_at)?->toIso8601String(),
            'retry_url' => route('enhance.retry', ['enhanceJob' => $job]),
            'delete_url' => route('enhance.destroy', ['enhanceJob' => $job]),
            'download_url' => $job->outputUrl(),
            'can_retry' => auth()->user()?->can('retry', $job) ?? false,
            'can_delete' => auth()->user()?->can('delete', $job) ?? false,
        ];
    }
}