<?php

declare(strict_types=1);

namespace App\Services;

use App\Models\Artwork;
use App\Models\Collection;
use App\Models\Group;
use App\Models\HomepageAnnouncement;
use App\Models\User;
use App\Models\World;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Storage;
use cPad\Plugins\News\Models\NewsArticle;

class HomepageAnnouncementService
{
    public const ACTIVE_CACHE_KEY = 'skinbase:homepage:announcement:active';

    public function __construct(private readonly HomepageAnnouncementSanitizer $sanitizer)
    {
    }

    public function getActiveForHomepage(): ?HomepageAnnouncement
    {
        return Cache::remember(self::ACTIVE_CACHE_KEY, now()->addMinutes(5), function (): ?HomepageAnnouncement {
            return HomepageAnnouncement::query()
                ->published()
                ->active()
                ->visibleNow()
                ->forPlacement(HomepageAnnouncement::PLACEMENT_HOMEPAGE_AFTER_FEATURED)
                ->orderByDesc('priority')
                ->orderByDesc('starts_at')
                ->first();
        });
    }

    public function clearActiveCache(): void
    {
        Cache::forget(self::ACTIVE_CACHE_KEY);
    }

    /**
     * @param  array<string, mixed>  $attributes
     * @return array<string, mixed>
     */
    public function sanitizeAttributes(array $attributes): array
    {
        foreach (['primary_link_url', 'secondary_link_url'] as $key) {
            $attributes[$key] = $this->sanitizer->sanitizeCustomUrl($attributes[$key] ?? null);
        }

        $attributes['content_html'] = $this->sanitizer->sanitizeHtml($attributes['content_html'] ?? null);

        return $attributes;
    }

    public function toHomepagePayload(?HomepageAnnouncement $announcement): ?array
    {
        if (! $announcement) {
            return null;
        }

        return [
            'id' => (int) $announcement->id,
            'dismiss_version' => max(1, (int) $announcement->dismiss_version),
            'title' => (string) $announcement->title,
            'subtitle' => $announcement->subtitle,
            'badge_text' => $announcement->badge_text,
            'content_html' => $announcement->content_html,
            'gradient_preset' => $announcement->gradient_preset,
            'theme_preset' => $announcement->theme_preset,
            'background_image_url' => $this->resolveBackgroundImageUrl($announcement->background_image),
            'is_dismissible' => (bool) $announcement->is_dismissible,
            'overlay_opacity' => (int) ($announcement->overlay_opacity ?? 55),
            'primary_link' => $this->buildLinkPayload($announcement, 'primary'),
            'secondary_link' => $this->buildLinkPayload($announcement, 'secondary'),
        ];
    }

    /**
     * @param  array<string, mixed>  $attributes
     */
    public function previewPayload(array $attributes): ?array
    {
        $announcement = new HomepageAnnouncement();
        $announcement->forceFill($this->sanitizeAttributes($attributes));
        $announcement->id = 0;

        return $this->toHomepagePayload($announcement);
    }

    private function buildLinkPayload(HomepageAnnouncement $announcement, string $prefix): ?array
    {
        $label = trim((string) $announcement->getAttribute($prefix . '_link_label'));
        if ($label === '') {
            return null;
        }

        $url = $this->resolveLinkUrl($announcement, $prefix);
        if ($url === null) {
            return null;
        }

        return [
            'label' => $label,
            'url' => $url,
        ];
    }

    private function resolveLinkUrl(HomepageAnnouncement $announcement, string $prefix): ?string
    {
        $type = (string) ($announcement->getAttribute($prefix . '_link_type') ?? HomepageAnnouncement::LINK_TYPE_NONE);
        $url = $this->sanitizer->sanitizeCustomUrl($announcement->getAttribute($prefix . '_link_url'));
        $targetId = (int) ($announcement->getAttribute($prefix . '_link_target_id') ?? 0);

        return match ($type) {
            HomepageAnnouncement::LINK_TYPE_NONE => null,
            HomepageAnnouncement::LINK_TYPE_CUSTOM_URL => $url,
            HomepageAnnouncement::LINK_TYPE_EXPLORE => Route::has('explore.index') ? route('explore.index') : ($url ?? '/explore'),
            HomepageAnnouncement::LINK_TYPE_UPLOAD => Route::has('upload') ? route('upload') : ($url ?? '/upload'),
            HomepageAnnouncement::LINK_TYPE_NEWS => $this->newsUrl($targetId) ?? $url,
            HomepageAnnouncement::LINK_TYPE_WORLD => $this->worldUrl($targetId) ?? $url,
            HomepageAnnouncement::LINK_TYPE_COLLECTION => $this->collectionUrl($targetId) ?? $url,
            HomepageAnnouncement::LINK_TYPE_GROUP => $this->groupUrl($targetId) ?? $url,
            HomepageAnnouncement::LINK_TYPE_PROFILE => $this->profileUrl($targetId) ?? $url,
            HomepageAnnouncement::LINK_TYPE_ARTWORK => $this->artworkUrl($targetId) ?? $url,
            default => $url,
        };
    }

    private function resolveBackgroundImageUrl(?string $backgroundImage): ?string
    {
        $backgroundImage = trim((string) $backgroundImage);
        if ($backgroundImage === '') {
            return null;
        }

        if (str_starts_with($backgroundImage, 'http://') || str_starts_with($backgroundImage, 'https://') || str_starts_with($backgroundImage, '/')) {
            return $backgroundImage;
        }

        $disk = $this->backgroundImageDisk();
        $configuredBaseUrl = trim((string) config('filesystems.disks.' . $disk . '.url', ''), '/');

        if ($configuredBaseUrl !== '') {
            return $configuredBaseUrl . '/' . ltrim($backgroundImage, '/');
        }

        return Storage::disk($disk)->url($backgroundImage);
    }

    public function backgroundImageDisk(): string
    {
        return (string) config('homepage.announcements.background_image.disk', config('uploads.object_storage.disk', 's3'));
    }

    public function backgroundImagePrefix(): string
    {
        return trim((string) config('homepage.announcements.background_image.prefix', 'homepage-announcements'), '/');
    }

    private function artworkUrl(int $artworkId): ?string
    {
        if ($artworkId < 1) {
            return null;
        }

        $artwork = Artwork::query()->find($artworkId);
        if (! $artwork) {
            return null;
        }

        return null;
    }

    private function newsUrl(int $articleId): ?string
    {
        if ($articleId < 1) {
            return null;
        }

        $article = NewsArticle::query()->find($articleId);
        if (! $article) {
            return null;
        }

        return route('news.show', ['slug' => $article->slug]);
    }

    private function worldUrl(int $worldId): ?string
    {
        if ($worldId < 1) {
            return null;
        }

        $world = World::query()->find($worldId);
        if (! $world) {
            return null;
        }

        if (method_exists($world, 'publicUrl')) {
            return $world->publicUrl();
        }

        return route('worlds.show', ['world' => $world->slug]);
    }

    private function collectionUrl(int $collectionId): ?string
    {
        if ($collectionId < 1) {
            return null;
        }

        $collection = Collection::query()->with('user')->find($collectionId);
        if (! $collection || ! $collection->user?->username) {
            return null;
        }

        return route('profile.collections.show', [
            'username' => strtolower((string) $collection->user->username),
            'slug' => $collection->slug,
        ]);
    }

    private function groupUrl(int $groupId): ?string
    {
        if ($groupId < 1) {
            return null;
        }

        $group = Group::query()->find($groupId);
        if (! $group) {
            return null;
        }

        return route('groups.show', ['group' => $group]);
    }

    private function profileUrl(int $userId): ?string
    {
        if ($userId < 1) {
            return null;
        }

        $user = User::query()->find($userId);
        if (! $user?->username) {
            return null;
        }

        return route('profile.show', ['username' => strtolower((string) $user->username)]);
    }
}