<?php

declare(strict_types=1);

namespace App\Http\Controllers\Settings;

use App\Http\Controllers\Controller;
use App\Http\Requests\Settings\UpsertHomepageAnnouncementRequest;
use App\Models\HomepageAnnouncement;
use App\Services\HomepageAnnouncementService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Inertia\Inertia;
use Inertia\Response;

class HomepageAnnouncementController extends Controller
{
    private const BACKGROUND_WEBP_QUALITY = 84;

    public function __construct(private readonly HomepageAnnouncementService $announcements)
    {
    }

    public function index(Request $request): Response
    {
        $announcements = HomepageAnnouncement::query()
            ->latest('updated_at')
            ->paginate(20)
            ->withQueryString();

        $announcements->getCollection()->transform(fn (HomepageAnnouncement $announcement): array => $this->serializeForIndex($announcement));

        return Inertia::render('Admin/HomepageAnnouncements/Index', [
            'announcements' => $announcements,
            'createUrl' => route('admin.homepage-announcements.create'),
        ]);
    }

    public function create(): Response
    {
        $form = $this->blankForm();

        return Inertia::render('Admin/HomepageAnnouncements/Form', [
            'announcement' => $form,
            'previewAnnouncement' => $this->announcements->previewPayload($form),
            'options' => $this->options(),
            'submitUrl' => route('admin.homepage-announcements.store'),
            'previewUrl' => route('admin.homepage-announcements.preview'),
            'indexUrl' => route('admin.homepage-announcements.index'),
            'destroyUrl' => null,
        ]);
    }

    public function store(UpsertHomepageAnnouncementRequest $request): RedirectResponse
    {
        $actor = $this->currentActor($request);
        $announcement = new HomepageAnnouncement();
        $attributes = $this->persistedAttributes($request, $announcement);
        $attributes['created_by'] = (int) $actor->id;
        $attributes['updated_by'] = (int) $actor->id;

        $announcement->forceFill($attributes)->save();

        return redirect()
            ->route('admin.homepage-announcements.edit', ['homepageAnnouncement' => $announcement])
            ->with('success', 'Homepage announcement created.');
    }

    public function edit(HomepageAnnouncement $homepageAnnouncement): Response
    {
        $form = $this->serializeForForm($homepageAnnouncement);

        return Inertia::render('Admin/HomepageAnnouncements/Form', [
            'announcement' => $form,
            'previewAnnouncement' => $this->announcements->toHomepagePayload($homepageAnnouncement),
            'options' => $this->options(),
            'submitUrl' => route('admin.homepage-announcements.update', ['homepageAnnouncement' => $homepageAnnouncement]),
            'previewUrl' => route('admin.homepage-announcements.preview'),
            'indexUrl' => route('admin.homepage-announcements.index'),
            'destroyUrl' => route('admin.homepage-announcements.destroy', ['homepageAnnouncement' => $homepageAnnouncement]),
        ]);
    }

    public function update(UpsertHomepageAnnouncementRequest $request, HomepageAnnouncement $homepageAnnouncement): RedirectResponse
    {
        $actor = $this->currentActor($request);
        $attributes = $this->persistedAttributes($request, $homepageAnnouncement);
        $attributes['updated_by'] = (int) $actor->id;

        $homepageAnnouncement->forceFill($attributes)->save();

        return redirect()
            ->route('admin.homepage-announcements.edit', ['homepageAnnouncement' => $homepageAnnouncement])
            ->with('success', 'Homepage announcement updated.');
    }

    public function destroy(HomepageAnnouncement $homepageAnnouncement): RedirectResponse
    {
        $homepageAnnouncement->delete();

        return redirect()
            ->route('admin.homepage-announcements.index')
            ->with('success', 'Homepage announcement deleted.');
    }

    public function preview(UpsertHomepageAnnouncementRequest $request): JsonResponse
    {
        $attributes = $this->persistedAttributes($request, null, false);

        return response()->json([
            'ok' => true,
            'announcement' => $this->announcements->previewPayload($attributes),
        ]);
    }

    /**
     * @return array<string, mixed>
     */
    private function persistedAttributes(UpsertHomepageAnnouncementRequest $request, ?HomepageAnnouncement $announcement = null, bool $persistFile = true): array
    {
        $validated = $request->validated();
        unset($validated['background_image_file'], $validated['remove_background_image']);

        $validated = $this->announcements->sanitizeAttributes($validated);

        $validated = $this->normalizeLinkAttributes($validated, 'primary');
        $validated = $this->normalizeLinkAttributes($validated, 'secondary');

        $currentBackground = $announcement?->background_image;
        $backgroundImageFile = $this->backgroundImageUpload($request);

        if ($request->boolean('remove_background_image')) {
            if ($persistFile) {
                $this->deleteStoredBackgroundIfLocal($currentBackground);
            }

            $validated['background_image'] = null;
        } elseif ($persistFile && $backgroundImageFile instanceof UploadedFile) {
            $this->deleteStoredBackgroundIfLocal($currentBackground);
            $validated['background_image'] = $this->storeBackgroundImage($backgroundImageFile);
        } elseif (! array_key_exists('background_image', $validated) && $announcement) {
            $validated['background_image'] = $announcement->background_image;
        }

        $validated['background_type'] = filled($validated['background_image'] ?? null) ? 'image' : null;

        return $validated;
    }

    /**
     * @param  array<string, mixed>  $attributes
     * @return array<string, mixed>
     */
    private function normalizeLinkAttributes(array $attributes, string $prefix): array
    {
        $type = (string) ($attributes[$prefix . '_link_type'] ?? HomepageAnnouncement::LINK_TYPE_NONE);

        if ($type === HomepageAnnouncement::LINK_TYPE_NONE) {
            $attributes[$prefix . '_link_label'] = null;
            $attributes[$prefix . '_link_url'] = null;
            $attributes[$prefix . '_link_target_type'] = null;
            $attributes[$prefix . '_link_target_id'] = null;

            return $attributes;
        }

        $attributes[$prefix . '_link_label'] = filled($attributes[$prefix . '_link_label'] ?? null)
            ? trim((string) $attributes[$prefix . '_link_label'])
            : null;
        $attributes[$prefix . '_link_url'] = filled($attributes[$prefix . '_link_url'] ?? null)
            ? trim((string) $attributes[$prefix . '_link_url'])
            : null;
        $attributes[$prefix . '_link_target_id'] = filled($attributes[$prefix . '_link_target_id'] ?? null)
            ? (int) $attributes[$prefix . '_link_target_id']
            : null;

        if (in_array($type, [HomepageAnnouncement::LINK_TYPE_CUSTOM_URL, HomepageAnnouncement::LINK_TYPE_EXPLORE, HomepageAnnouncement::LINK_TYPE_UPLOAD], true)) {
            $attributes[$prefix . '_link_target_type'] = null;
            $attributes[$prefix . '_link_target_id'] = null;

            return $attributes;
        }

        $attributes[$prefix . '_link_target_type'] = $type;

        return $attributes;
    }

    private function deleteStoredBackgroundIfLocal(?string $path): void
    {
        $path = trim((string) $path);
        if ($path === '' || str_starts_with($path, 'http://') || str_starts_with($path, 'https://') || str_starts_with($path, '/')) {
            return;
        }

        $backgroundDisk = $this->announcements->backgroundImageDisk();

        if (Storage::disk($backgroundDisk)->exists($path)) {
            Storage::disk($backgroundDisk)->delete($path);

            return;
        }

        if (Storage::disk('public')->exists($path)) {
            Storage::disk('public')->delete($path);
        }
    }

    private function backgroundImageUpload(UpsertHomepageAnnouncementRequest $request): ?UploadedFile
    {
        $file = $request->file('background_image_file');

        if (! $file instanceof UploadedFile) {
            return null;
        }

        $pathName = trim((string) $file->getPathname());
        if ($file->isValid() && $pathName !== '' && is_file($pathName) && is_readable($pathName)) {
            return $file;
        }

        throw ValidationException::withMessages([
            'background_image_file' => $this->backgroundUploadErrorMessage($file),
        ]);
    }

    private function storeBackgroundImage(UploadedFile $file): string
    {
        $pathName = trim((string) $file->getPathname());
        if ($pathName === '' || ! is_file($pathName) || ! is_readable($pathName)) {
            throw ValidationException::withMessages([
                'background_image_file' => $this->backgroundUploadErrorMessage($file),
            ]);
        }

        if (! function_exists('imagecreatefromstring') || ! function_exists('imagewebp')) {
            throw ValidationException::withMessages([
                'background_image_file' => 'The server is missing WebP image support. Enable the GD WebP extension to upload announcement backgrounds.',
            ]);
        }

        $binary = @file_get_contents($pathName);
        if ($binary === false) {
            throw ValidationException::withMessages([
                'background_image_file' => 'The uploaded background image could not be opened for conversion. Please choose the file again and retry.',
            ]);
        }

        $image = @imagecreatefromstring($binary);
        if (! $image instanceof \GdImage) {
            throw ValidationException::withMessages([
                'background_image_file' => 'The uploaded background image format could not be converted. Please use JPG, PNG, or WEBP.',
            ]);
        }

        try {
            if (! imageistruecolor($image)) {
                imagepalettetotruecolor($image);
            }

            imagealphablending($image, true);
            imagesavealpha($image, true);

            ob_start();
            $converted = imagewebp($image, null, self::BACKGROUND_WEBP_QUALITY);
            $webpBinary = ob_get_clean();

            if (! $converted || ! is_string($webpBinary) || $webpBinary === '') {
                throw ValidationException::withMessages([
                    'background_image_file' => 'The uploaded background image could not be converted to WebP. Please try a different image.',
                ]);
            }

            $storedPath = $this->announcements->backgroundImagePrefix() . '/' . pathinfo(Str::replace('\\', '/', $file->hashName()), PATHINFO_FILENAME) . '.webp';
            Storage::disk($this->announcements->backgroundImageDisk())->put($storedPath, $webpBinary, ['visibility' => 'public']);
        } finally {
            imagedestroy($image);
        }

        return $storedPath;
    }

    private function backgroundUploadErrorMessage(UploadedFile $file): string
    {
        return match ($file->getError()) {
            UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE => 'The uploaded background image exceeds the server upload limit.',
            UPLOAD_ERR_PARTIAL => 'The uploaded background image was only partially received. Please retry the upload.',
            UPLOAD_ERR_NO_TMP_DIR => 'The server upload temp directory is unavailable. Check PHP upload temp configuration.',
            UPLOAD_ERR_CANT_WRITE => 'The server could not write the uploaded background image to temporary storage.',
            UPLOAD_ERR_EXTENSION => 'A PHP extension blocked the background image upload.',
            default => 'The uploaded background image could not be read. Please choose the file again and retry.',
        };
    }

    /**
     * @return array<string, mixed>
     */
    private function blankForm(): array
    {
        return [
            'id' => null,
            'title' => '',
            'subtitle' => '',
            'badge_text' => '',
            'content_html' => '',
            'type' => HomepageAnnouncement::TYPE_ANNOUNCEMENT,
            'status' => HomepageAnnouncement::STATUS_DRAFT,
            'is_active' => true,
            'starts_at' => '',
            'ends_at' => '',
            'priority' => 0,
            'is_dismissible' => true,
            'dismiss_version' => 1,
            'gradient_preset' => HomepageAnnouncement::GRADIENT_NOVA_AURORA,
            'theme_preset' => 'launch',
            'placement' => HomepageAnnouncement::PLACEMENT_HOMEPAGE_AFTER_FEATURED,
            'text_color' => '',
            'overlay_opacity' => 55,
            'background_image' => '',
            'background_image_url' => null,
            'remove_background_image' => false,
            'background_image_file' => null,
            'primary_link_label' => '',
            'primary_link_type' => HomepageAnnouncement::LINK_TYPE_NONE,
            'primary_link_url' => '',
            'primary_link_target_id' => '',
            'secondary_link_label' => '',
            'secondary_link_type' => HomepageAnnouncement::LINK_TYPE_NONE,
            'secondary_link_url' => '',
            'secondary_link_target_id' => '',
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function serializeForForm(HomepageAnnouncement $announcement): array
    {
        return [
            'id' => (int) $announcement->id,
            'title' => (string) $announcement->title,
            'subtitle' => (string) ($announcement->subtitle ?? ''),
            'badge_text' => (string) ($announcement->badge_text ?? ''),
            'content_html' => (string) ($announcement->content_html ?? ''),
            'type' => (string) $announcement->type,
            'status' => (string) $announcement->status,
            'is_active' => (bool) $announcement->is_active,
            'starts_at' => optional($announcement->starts_at)?->format('Y-m-d\TH:i') ?? '',
            'ends_at' => optional($announcement->ends_at)?->format('Y-m-d\TH:i') ?? '',
            'priority' => (int) $announcement->priority,
            'is_dismissible' => (bool) $announcement->is_dismissible,
            'dismiss_version' => (int) $announcement->dismiss_version,
            'gradient_preset' => (string) ($announcement->gradient_preset ?? ''),
            'theme_preset' => (string) ($announcement->theme_preset ?? ''),
            'placement' => (string) $announcement->placement,
            'text_color' => (string) ($announcement->text_color ?? ''),
            'overlay_opacity' => (int) ($announcement->overlay_opacity ?? 55),
            'background_image' => (string) ($announcement->background_image ?? ''),
            'background_image_url' => $this->announcements->toHomepagePayload($announcement)['background_image_url'] ?? null,
            'remove_background_image' => false,
            'background_image_file' => null,
            'primary_link_label' => (string) ($announcement->primary_link_label ?? ''),
            'primary_link_type' => (string) ($announcement->primary_link_type ?? HomepageAnnouncement::LINK_TYPE_NONE),
            'primary_link_url' => (string) ($announcement->primary_link_url ?? ''),
            'primary_link_target_id' => $announcement->primary_link_target_id ? (string) $announcement->primary_link_target_id : '',
            'secondary_link_label' => (string) ($announcement->secondary_link_label ?? ''),
            'secondary_link_type' => (string) ($announcement->secondary_link_type ?? HomepageAnnouncement::LINK_TYPE_NONE),
            'secondary_link_url' => (string) ($announcement->secondary_link_url ?? ''),
            'secondary_link_target_id' => $announcement->secondary_link_target_id ? (string) $announcement->secondary_link_target_id : '',
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function serializeForIndex(HomepageAnnouncement $announcement): array
    {
        return [
            'id' => (int) $announcement->id,
            'title' => (string) $announcement->title,
            'type' => (string) $announcement->type,
            'status' => (string) $announcement->status,
            'is_active' => (bool) $announcement->is_active,
            'priority' => (int) $announcement->priority,
            'dismiss_version' => (int) $announcement->dismiss_version,
            'placement' => (string) $announcement->placement,
            'starts_at' => optional($announcement->starts_at)?->toIso8601String(),
            'ends_at' => optional($announcement->ends_at)?->toIso8601String(),
            'updated_at' => optional($announcement->updated_at)?->toIso8601String(),
            'edit_url' => route('admin.homepage-announcements.edit', ['homepageAnnouncement' => $announcement]),
            'destroy_url' => route('admin.homepage-announcements.destroy', ['homepageAnnouncement' => $announcement]),
            'preview' => $this->announcements->toHomepagePayload($announcement),
        ];
    }

    /**
     * @return array<string, array<int, array<string, string>>>
     */
    private function options(): array
    {
        return [
            'types' => [
                ['value' => HomepageAnnouncement::TYPE_ANNOUNCEMENT, 'label' => 'Announcement'],
                ['value' => HomepageAnnouncement::TYPE_LAUNCH, 'label' => 'Launch'],
                ['value' => HomepageAnnouncement::TYPE_NEWS, 'label' => 'News'],
                ['value' => HomepageAnnouncement::TYPE_WORLD, 'label' => 'World'],
                ['value' => HomepageAnnouncement::TYPE_EVENT, 'label' => 'Event'],
                ['value' => HomepageAnnouncement::TYPE_NOTICE, 'label' => 'Notice'],
                ['value' => HomepageAnnouncement::TYPE_MAINTENANCE, 'label' => 'Maintenance'],
            ],
            'statuses' => [
                ['value' => HomepageAnnouncement::STATUS_DRAFT, 'label' => 'Draft'],
                ['value' => HomepageAnnouncement::STATUS_PUBLISHED, 'label' => 'Published'],
                ['value' => HomepageAnnouncement::STATUS_ARCHIVED, 'label' => 'Archived'],
            ],
            'placements' => [
                ['value' => HomepageAnnouncement::PLACEMENT_HOMEPAGE_AFTER_FEATURED, 'label' => 'Homepage after Featured Artwork'],
            ],
            'linkTypes' => [
                ['value' => HomepageAnnouncement::LINK_TYPE_NONE, 'label' => 'None'],
                ['value' => HomepageAnnouncement::LINK_TYPE_CUSTOM_URL, 'label' => 'Custom URL'],
                ['value' => HomepageAnnouncement::LINK_TYPE_NEWS, 'label' => 'News article'],
                ['value' => HomepageAnnouncement::LINK_TYPE_WORLD, 'label' => 'World'],
                ['value' => HomepageAnnouncement::LINK_TYPE_ARTWORK, 'label' => 'Artwork'],
                ['value' => HomepageAnnouncement::LINK_TYPE_COLLECTION, 'label' => 'Collection'],
                ['value' => HomepageAnnouncement::LINK_TYPE_GROUP, 'label' => 'Group'],
                ['value' => HomepageAnnouncement::LINK_TYPE_PROFILE, 'label' => 'Profile'],
                ['value' => HomepageAnnouncement::LINK_TYPE_EXPLORE, 'label' => 'Explore'],
                ['value' => HomepageAnnouncement::LINK_TYPE_UPLOAD, 'label' => 'Upload'],
            ],
            'gradients' => [
                ['value' => HomepageAnnouncement::GRADIENT_NOVA_AURORA, 'label' => 'Nova Aurora'],
                ['value' => HomepageAnnouncement::GRADIENT_DEEP_SPACE, 'label' => 'Deep Space'],
                ['value' => HomepageAnnouncement::GRADIENT_SUNRISE, 'label' => 'Sunrise'],
                ['value' => HomepageAnnouncement::GRADIENT_OCEAN_GLOW, 'label' => 'Ocean Glow'],
                ['value' => HomepageAnnouncement::GRADIENT_SPRING_VIBES, 'label' => 'Spring Vibes'],
                ['value' => HomepageAnnouncement::GRADIENT_FANTASY_REALMS, 'label' => 'Fantasy Realms'],
                ['value' => HomepageAnnouncement::GRADIENT_MINIMAL_LIGHT, 'label' => 'Minimal Light'],
                ['value' => HomepageAnnouncement::GRADIENT_DARK_GLASS, 'label' => 'Dark Glass'],
            ],
            'themes' => [
                ['value' => 'launch', 'label' => 'Launch'],
                ['value' => 'announcement', 'label' => 'Announcement'],
                ['value' => 'notice', 'label' => 'Notice'],
                ['value' => 'maintenance', 'label' => 'Maintenance'],
            ],
        ];
    }

    private function currentActor(Request $request): object
    {
        return $request->user('controlpanel') ?? $request->user() ?? abort(403, 'Admin access required.');
    }
}