<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Str;
use Carbon\Carbon;

class Quiz extends Model
{
    use HasFactory, SoftDeletes;

    protected $fillable = [
        'title',
        'description',
        'slug',
        'unique_url',
        'require_auth',
        'allow_anonymous',
        'settings',
        'metadata',
        'i18n',
        'status',
        'visibility',
        'version',
        'parent_id',
        'is_active',
        'published_at',
        'expires_at',
        'created_by',
        'updated_by',
    ];

    protected $casts = [
        'settings' => 'array',
        'metadata' => 'array',
        'i18n' => 'array',
        'is_active' => 'boolean',
        'require_auth' => 'boolean',
        'allow_anonymous' => 'boolean',
        'published_at' => 'datetime',
        'expires_at' => 'datetime',
    ];

    protected $appends = [
        'total_questions',
        'total_points',
        'duration_minutes',
        'is_published',
        'is_expired',
        'creator',
    ];

    // Relationships
    public function questions(): HasMany
    {
        return $this->hasMany(QuizQuestion::class)->where('is_active', true)->orderBy('order');
    }

    public function allQuestions(): HasMany
    {
        return $this->hasMany(QuizQuestion::class)->orderBy('order');
    }

    public function attempts(): HasMany
    {
        return $this->hasMany(QuizAttempt::class);
    }

    public function analytics(): HasMany
    {
        return $this->hasMany(QuizAnalytic::class);
    }

    public function creator(): BelongsTo
    {
        return $this->belongsTo(User::class, 'created_by');
    }

    public function updater(): BelongsTo
    {
        return $this->belongsTo(User::class, 'updated_by');
    }

    public function parent(): BelongsTo
    {
        return $this->belongsTo(Quiz::class, 'parent_id');
    }

    public function versions(): HasMany
    {
        return $this->hasMany(Quiz::class, 'parent_id');
    }

    public function invitations(): HasMany
    {
        return $this->hasMany(QuizInvitation::class);
    }

    // Scopes
    public function scopePublished(Builder $query): Builder
    {
        return $query->where('status', 'published')
                    ->where('is_active', true)
                    ->where(function ($q) {
                        $q->whereNull('expires_at')
                          ->orWhere('expires_at', '>', now());
                    });
    }

    public function scopePublic(Builder $query): Builder
    {
        return $query->where('visibility', 'public');
    }

    public function scopeByUser(Builder $query, int $userId): Builder
    {
        return $query->where('created_by', $userId);
    }

    public function scopeActive(Builder $query): Builder
    {
        return $query->where('is_active', true);
    }

    // Accessors
    public function getTotalQuestionsAttribute(): int
    {
        return $this->questions()->count();
    }

    public function getTotalPointsAttribute(): float
    {
        return $this->questions()->sum('points');
    }

    public function getDurationMinutesAttribute(): ?int
    {
        return $this->settings['duration_minutes'] ?? null;
    }

    public function getIsPublishedAttribute(): bool
    {
        return $this->status === 'published' && 
               $this->is_active && 
               $this->published_at !== null;
    }

    public function getIsExpiredAttribute(): bool
    {
        return $this->expires_at !== null && $this->expires_at->isPast();
    }

    // Methods
    public function generateSlug(): string
    {
        $baseSlug = Str::slug($this->title);
        $slug = $baseSlug;
        $counter = 1;

        while (static::where('slug', $slug)->where('id', '!=', $this->id)->exists()) {
            $slug = $baseSlug . '-' . $counter;
            $counter++;
        }

        return $slug;
    }

    public function generateUniqueUrl(): string
    {
        $uniqueUrl = Str::random(16);
        
        while (static::where('unique_url', $uniqueUrl)->where('id', '!=', $this->id)->exists()) {
            $uniqueUrl = Str::random(16);
        }

        return $uniqueUrl;
    }

    public function getPublicUrlAttribute(): string
    {
        return route('quiz.public', ['quiz' => $this->unique_url]);
    }

    public function createVersion(array $data = []): self
    {
        $versionData = array_merge($this->toArray(), $data, [
            'version' => $this->version + 1,
            'parent_id' => $this->parent_id ?: $this->id,
            'status' => 'draft',
            'published_at' => null,
            'created_by' => auth()->id(),
            'updated_by' => auth()->id(),
        ]);

        unset($versionData['id'], $versionData['created_at'], $versionData['updated_at']);

        return static::create($versionData);
    }

    public function canBeAttemptedBy(User $user): bool
    {
        // Check if quiz is published and not expired
        if (!$this->is_published || $this->is_expired) {
            return false;
        }

        // Check visibility
        if ($this->visibility === 'private' && $this->created_by !== $user->id) {
            return false;
        }

        // Check attempt limits
        $maxAttempts = $this->settings['max_attempts'] ?? null;
        if ($maxAttempts) {
            $userAttempts = $this->attempts()
                ->where('user_id', $user->id)
                ->whereIn('status', ['completed', 'abandoned'])
                ->count();
            
            if ($userAttempts >= $maxAttempts) {
                return false;
            }
        }

        return true;
    }

    public function getAvailableAttempts(User $user): int
    {
        $maxAttempts = $this->settings['max_attempts'] ?? PHP_INT_MAX;
        $usedAttempts = $this->attempts()
            ->where('user_id', $user->id)
            ->whereIn('status', ['completed', 'abandoned'])
            ->count();

        return max(0, $maxAttempts - $usedAttempts);
    }

    public function getInProgressAttempt(User $user): ?QuizAttempt
    {
        return $this->attempts()
            ->where('user_id', $user->id)
            ->where('status', 'in_progress')
            ->where(function ($query) {
                $query->whereNull('expires_at')
                      ->orWhere('expires_at', '>', now());
            })
            ->first();
    }

    public function getQuestionOrder(): array
    {
        return $this->questions()->pluck('id')->toArray();
    }

    public function getSettings(string $key = null, $default = null)
    {
        if ($key === null) {
            return $this->settings ?? [];
        }

        return data_get($this->settings, $key, $default);
    }

    public function setSettings(array $settings): void
    {
        $this->update(['settings' => array_merge($this->settings ?? [], $settings)]);
    }

    public function getI18nData(string $locale = null, string $key = null)
    {
        $locale = $locale ?? app()->getLocale();
        $i18nData = $this->i18n ?? [];

        if ($key === null) {
            return $i18nData[$locale] ?? [];
        }

        return data_get($i18nData, "{$locale}.{$key}");
    }

    public function setI18nData(string $locale, array $data): void
    {
        $i18nData = $this->i18n ?? [];
        $i18nData[$locale] = array_merge($i18nData[$locale] ?? [], $data);
        $this->update(['i18n' => $i18nData]);
    }

    // Boot method
    protected static function boot()
    {
        parent::boot();

        static::creating(function ($quiz) {
            if (empty($quiz->slug)) {
                $quiz->slug = $quiz->generateSlug();
            }
            if (empty($quiz->unique_url)) {
                $quiz->unique_url = $quiz->generateUniqueUrl();
            }
        });

        static::updating(function ($quiz) {
            if ($quiz->isDirty('title') && empty($quiz->slug)) {
                $quiz->slug = $quiz->generateSlug();
            }
        });
    }

    /**
     * Get the creator attribute.
     */
    public function getCreatorAttribute()
    {
        return $this->createdBy;
    }
}