<?php

declare(strict_types=1);

namespace App\Services\Academy;

use App\Models\AcademyChallenge;
use App\Models\AcademyLesson;
use App\Models\AcademyPromptPack;
use App\Models\AcademyPromptTemplate;
use App\Models\User;
use Illuminate\Support\Str;

final class AcademyAccessService
{
    public function canAccessContent(?User $user, string $accessLevel): bool
    {
        if ($accessLevel === 'free') {
            return true;
        }

        if ($user === null) {
            return false;
        }

        if ($user->isAdmin()) {
            return true;
        }

        return $this->rankForUser($user) >= $this->rankForLevel($accessLevel);
    }

    public function canAccessLesson(?User $user, AcademyLesson $lesson): bool
    {
        return $this->canAccessContent($user, (string) $lesson->access_level);
    }

    public function canAccessPrompt(?User $user, AcademyPromptTemplate $prompt): bool
    {
        return $this->canAccessContent($user, (string) $prompt->access_level);
    }

    public function canAccessPack(?User $user, AcademyPromptPack $pack): bool
    {
        return $this->canAccessContent($user, (string) $pack->access_level);
    }

    public function canAccessChallenge(?User $user, AcademyChallenge $challenge): bool
    {
        return $this->canAccessContent($user, (string) $challenge->access_level);
    }

    public function lessonPayload(AcademyLesson $lesson, ?User $viewer, bool $includeFull = false): array
    {
        $authorized = $this->canAccessLesson($viewer, $lesson);
        $fullContent = (string) ($lesson->content ?? '');

        return [
            'id' => (int) $lesson->id,
            'title' => (string) $lesson->title,
            'slug' => (string) $lesson->slug,
            'excerpt' => (string) ($lesson->excerpt ?? ''),
            'content' => ($authorized && $includeFull) ? $fullContent : null,
            'content_preview' => $authorized ? null : $this->previewText($fullContent, 360),
            'difficulty' => (string) $lesson->difficulty,
            'access_level' => (string) $lesson->access_level,
            'lesson_type' => (string) $lesson->lesson_type,
            'cover_image' => $lesson->cover_image,
            'video_url' => $authorized ? $lesson->video_url : null,
            'reading_minutes' => (int) $lesson->reading_minutes,
            'featured' => (bool) $lesson->featured,
            'active' => (bool) $lesson->active,
            'published_at' => $lesson->published_at?->toISOString(),
            'category' => $lesson->category ? [
                'id' => (int) $lesson->category->id,
                'name' => (string) $lesson->category->name,
                'slug' => (string) $lesson->category->slug,
            ] : null,
            'locked' => ! $authorized,
            'can_access' => $authorized,
        ];
    }

    public function promptPayload(AcademyPromptTemplate $prompt, ?User $viewer, bool $includeFull = false): array
    {
        $authorized = $this->canAccessPrompt($viewer, $prompt);

        return [
            'id' => (int) $prompt->id,
            'title' => (string) $prompt->title,
            'slug' => (string) $prompt->slug,
            'excerpt' => (string) ($prompt->excerpt ?? ''),
            'prompt' => ($authorized && $includeFull) ? (string) $prompt->prompt : null,
            'negative_prompt' => ($authorized && $includeFull) ? (string) ($prompt->negative_prompt ?? '') : null,
            'usage_notes' => ($authorized && $includeFull) ? (string) ($prompt->usage_notes ?? '') : null,
            'workflow_notes' => ($authorized && $includeFull) ? (string) ($prompt->workflow_notes ?? '') : null,
            'prompt_preview' => $authorized ? null : $this->previewText((string) $prompt->prompt, 220),
            'difficulty' => (string) $prompt->difficulty,
            'access_level' => (string) $prompt->access_level,
            'aspect_ratio' => $prompt->aspect_ratio,
            'tags' => array_values((array) ($prompt->tags ?? [])),
            'tool_notes' => $authorized ? (array) ($prompt->tool_notes ?? []) : [],
            'preview_image' => $prompt->preview_image,
            'featured' => (bool) $prompt->featured,
            'prompt_of_week' => (bool) $prompt->prompt_of_week,
            'published_at' => $prompt->published_at?->toISOString(),
            'category' => $prompt->category ? [
                'id' => (int) $prompt->category->id,
                'name' => (string) $prompt->category->name,
                'slug' => (string) $prompt->category->slug,
            ] : null,
            'locked' => ! $authorized,
            'can_access' => $authorized,
        ];
    }

    public function packPayload(AcademyPromptPack $pack, ?User $viewer, bool $includePrompts = false): array
    {
        $authorized = $this->canAccessPack($viewer, $pack);

        return [
            'id' => (int) $pack->id,
            'title' => (string) $pack->title,
            'slug' => (string) $pack->slug,
            'excerpt' => (string) ($pack->excerpt ?? ''),
            'description' => (string) ($pack->description ?? ''),
            'access_level' => (string) $pack->access_level,
            'cover_image' => $pack->cover_image,
            'tags' => array_values((array) ($pack->tags ?? [])),
            'featured' => (bool) $pack->featured,
            'published_at' => $pack->published_at?->toISOString(),
            'locked' => ! $authorized,
            'can_access' => $authorized,
            'prompts' => $includePrompts
                ? $pack->prompts->map(fn (AcademyPromptTemplate $prompt): array => $this->promptPayload($prompt, $viewer, $authorized))->values()->all()
                : [],
        ];
    }

    public function challengePayload(AcademyChallenge $challenge, ?User $viewer, bool $includeSubmissions = false): array
    {
        $authorized = $this->canAccessChallenge($viewer, $challenge);

        return [
            'id' => (int) $challenge->id,
            'title' => (string) $challenge->title,
            'slug' => (string) $challenge->slug,
            'excerpt' => (string) ($challenge->excerpt ?? ''),
            'description' => (string) ($challenge->description ?? ''),
            'brief' => (string) ($challenge->brief ?? ''),
            'rules' => (string) ($challenge->rules ?? ''),
            'access_level' => (string) $challenge->access_level,
            'status' => (string) $challenge->status,
            'starts_at' => $challenge->starts_at?->toISOString(),
            'ends_at' => $challenge->ends_at?->toISOString(),
            'voting_starts_at' => $challenge->voting_starts_at?->toISOString(),
            'voting_ends_at' => $challenge->voting_ends_at?->toISOString(),
            'cover_image' => $challenge->cover_image,
            'prize_text' => $challenge->prize_text,
            'required_tags' => array_values((array) ($challenge->required_tags ?? [])),
            'allowed_categories' => array_values((array) ($challenge->allowed_categories ?? [])),
            'featured' => (bool) $challenge->featured,
            'locked' => ! $authorized,
            'can_access' => $authorized,
            'submission_count' => $includeSubmissions ? (int) $challenge->submissions()->approved()->count() : null,
        ];
    }

    private function rankForUser(User $user): int
    {
        if (method_exists($user, 'hasAcademyProAccess') && $user->hasAcademyProAccess()) {
            return $this->rankForLevel('pro');
        }

        if (method_exists($user, 'hasAcademyCreatorAccess') && $user->hasAcademyCreatorAccess()) {
            return $this->rankForLevel('creator');
        }

        return $this->rankForLevel('free');
    }

    private function rankForLevel(string $accessLevel): int
    {
        return match (Str::lower(trim($accessLevel))) {
            'admin' => 99,
            'pro' => 30,
            'creator' => 20,
            default => 10,
        };
    }

    private function previewText(string $value, int $limit): string
    {
        $plain = trim(strip_tags($value));

        if ($plain === '') {
            return '';
        }

        $length = mb_strlen($plain);
        $previewLength = min($limit, max(12, (int) ceil($length * 0.55)));

        if ($previewLength >= $length) {
            $previewLength = max(1, $length - 1);
        }

        return rtrim(mb_substr($plain, 0, $previewLength)) . '...';
    }
}