<?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\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Str;

class QuizQuestion extends Model
{
    use HasFactory, SoftDeletes;

    const QUESTION_TYPES = [
        'MULTIPLE_CHOICE' => 'Multiple Choice',
        'TRUE_FALSE' => 'True/False',
        'FILL_BLANK' => 'Fill in the Blank',
        'SHORT_ANSWER' => 'Short Answer',
        'ESSAY' => 'Essay',
        'MATCH' => 'Matching',
        'ORDER' => 'Ordering',
        'MATRIX' => 'Matrix',
        'FILE_UPLOAD' => 'File Upload',
        'DATE' => 'Date',
        'CODE' => 'Code',
        'NUMERIC' => 'Numeric',
        'RATING' => 'Rating Scale',
        'LIKERT' => 'Likert Scale',
    ];

    protected $fillable = [
        'quiz_id',
        'type',
        'question_text',
        'question_data',
        'answer_data',
        'scoring_data',
        'validation_rules',
        'metadata',
        'i18n',
        'order',
        'points',
        'is_required',
        'is_active',
        'version',
        'parent_id',
        'created_by',
        'updated_by',
    ];

    // Alias for compatibility
    public function getQuestionTypeAttribute()
    {
        return $this->type;
    }

    public function setQuestionTypeAttribute($value)
    {
        $this->type = $value;
    }

    public function getOptionsAttribute()
    {
        return $this->question_data['options'] ?? [];
    }

    public function setOptionsAttribute($value)
    {
        $this->question_data = array_merge($this->question_data ?? [], ['options' => $value]);
    }

    public function getCorrectAnswerAttribute()
    {
        return $this->answer_data['correct'] ?? [];
    }

    public function setCorrectAnswerAttribute($value)
    {
        $this->answer_data = array_merge($this->answer_data ?? [], ['correct' => $value]);
    }

    public function getScorePointsAttribute()
    {
        return $this->attributes['points'] ?? 0;
    }

    public function setScorePointsAttribute($value)
    {
        $this->attributes['points'] = $value;
    }

    protected $casts = [
        'question_data' => 'array',
        'answer_data' => 'array',
        'scoring_data' => 'array',
        'validation_rules' => 'array',
        'metadata' => 'array',
        'i18n' => 'array',
        'points' => 'decimal:2',
        'is_required' => 'boolean',
        'is_active' => 'boolean',
    ];

    protected $appends = [
        'type_label',
        'has_multiple_answers',
        'is_scored',
        'points',
    ];

    // Relationships
    public function quiz(): BelongsTo
    {
        return $this->belongsTo(Quiz::class);
    }

    public function answers(): HasMany
    {
        return $this->hasMany(QuizAttemptAnswer::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(QuizQuestion::class, 'parent_id');
    }

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

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

    public function scopeByType(Builder $query, string $type): Builder
    {
        return $query->where('type', $type);
    }

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

    public function scopeOrdered(Builder $query): Builder
    {
        return $query->orderBy('order');
    }

    // Accessors
    public function getTypeLabelAttribute(): string
    {
        return self::QUESTION_TYPES[$this->type] ?? $this->type;
    }

    public function getHasMultipleAnswersAttribute(): bool
    {
        return in_array($this->type, ['MULTIPLE_CHOICE', 'MATCH', 'ORDER', 'MATRIX']);
    }

    public function getIsScoredAttribute(): bool
    {
        return $this->score_points > 0;
    }

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

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

        return static::create($versionData);
    }

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

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

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

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

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

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

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

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

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

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

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

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

    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]);
    }

    public function validateAnswer($answer): array
    {
        $validationRules = $this->getValidationRules();
        $errors = [];

        // Type-specific validation
        switch ($this->type) {
            case 'MULTIPLE_CHOICE':
                $errors = array_merge($errors, $this->validateMultipleChoice($answer));
                break;
            case 'TRUE_FALSE':
                $errors = array_merge($errors, $this->validateTrueFalse($answer));
                break;
            case 'FILL_BLANK':
                $errors = array_merge($errors, $this->validateFillBlank($answer));
                break;
            case 'SHORT_ANSWER':
                $errors = array_merge($errors, $this->validateShortAnswer($answer));
                break;
            case 'ESSAY':
                $errors = array_merge($errors, $this->validateEssay($answer));
                break;
            case 'MATCH':
                $errors = array_merge($errors, $this->validateMatch($answer));
                break;
            case 'ORDER':
                $errors = array_merge($errors, $this->validateOrder($answer));
                break;
            case 'MATRIX':
                $errors = array_merge($errors, $this->validateMatrix($answer));
                break;
            case 'FILE_UPLOAD':
                $errors = array_merge($errors, $this->validateFileUpload($answer));
                break;
            case 'DATE':
                $errors = array_merge($errors, $this->validateDate($answer));
                break;
            case 'CODE':
                $errors = array_merge($errors, $this->validateCode($answer));
                break;
            case 'NUMERIC':
                $errors = array_merge($errors, $this->validateNumeric($answer));
                break;
            case 'RATING':
            case 'LIKERT':
                $errors = array_merge($errors, $this->validateRating($answer));
                break;
        }

        return $errors;
    }

    private function validateMultipleChoice($answer): array
    {
        $errors = [];
        $options = $this->getAnswerData('options', []);

        if (empty($answer)) {
            if ($this->is_required) {
                $errors[] = 'This question is required.';
            }
            return $errors;
        }

        if (!is_array($answer)) {
            $answer = [$answer];
        }

        foreach ($answer as $selectedOption) {
            if (!in_array($selectedOption, array_column($options, 'value'))) {
                $errors[] = 'Invalid option selected.';
                break;
            }
        }

        return $errors;
    }

    private function validateTrueFalse($answer): array
    {
        $errors = [];

        if (empty($answer) && $this->is_required) {
            $errors[] = 'This question is required.';
            return $errors;
        }

        if (!in_array($answer, [true, false, 'true', 'false', 1, 0])) {
            $errors[] = 'Answer must be true or false.';
        }

        return $errors;
    }

    private function validateFillBlank($answer): array
    {
        $errors = [];
        $correctAnswers = $this->getAnswerData('correct_answers', []);

        if (empty($answer) && $this->is_required) {
            $errors[] = 'This question is required.';
            return $errors;
        }

        if (!is_array($answer)) {
            $answer = [$answer];
        }

        if (count($answer) !== count($correctAnswers)) {
            $errors[] = 'Number of answers does not match the number of blanks.';
        }

        return $errors;
    }

    private function validateShortAnswer($answer): array
    {
        $errors = [];
        $maxLength = $this->getValidationRules('max_length', 1000);

        if (empty($answer) && $this->is_required) {
            $errors[] = 'This question is required.';
            return $errors;
        }

        if (strlen($answer) > $maxLength) {
            $errors[] = "Answer must not exceed {$maxLength} characters.";
        }

        return $errors;
    }

    private function validateEssay($answer): array
    {
        $errors = [];
        $minLength = $this->getValidationRules('min_length', 0);
        $maxLength = $this->getValidationRules('max_length', 10000);

        if (empty($answer) && $this->is_required) {
            $errors[] = 'This question is required.';
            return $errors;
        }

        if (strlen($answer) < $minLength) {
            $errors[] = "Answer must be at least {$minLength} characters long.";
        }

        if (strlen($answer) > $maxLength) {
            $errors[] = "Answer must not exceed {$maxLength} characters.";
        }

        return $errors;
    }

    private function validateMatch($answer): array
    {
        $errors = [];
        $pairs = $this->getAnswerData('pairs', []);

        if (empty($answer) && $this->is_required) {
            $errors[] = 'This question is required.';
            return $errors;
        }

        if (!is_array($answer)) {
            $errors[] = 'Answer must be an array of pairs.';
            return $errors;
        }

        if (count($answer) !== count($pairs)) {
            $errors[] = 'Number of matches does not match the number of pairs.';
        }

        return $errors;
    }

    private function validateOrder($answer): array
    {
        $errors = [];
        $items = $this->getAnswerData('items', []);

        if (empty($answer) && $this->is_required) {
            $errors[] = 'This question is required.';
            return $errors;
        }

        if (!is_array($answer)) {
            $errors[] = 'Answer must be an array.';
            return $errors;
        }

        if (count($answer) !== count($items)) {
            $errors[] = 'Number of ordered items does not match the number of items.';
        }

        return $errors;
    }

    private function validateMatrix($answer): array
    {
        $errors = [];
        $matrix = $this->getAnswerData('matrix', []);

        if (empty($answer) && $this->is_required) {
            $errors[] = 'This question is required.';
            return $errors;
        }

        if (!is_array($answer)) {
            $errors[] = 'Answer must be a matrix (array of arrays).';
            return $errors;
        }

        return $errors;
    }

    private function validateFileUpload($answer): array
    {
        $errors = [];
        $allowedTypes = $this->getValidationRules('allowed_types', []);
        $maxSize = $this->getValidationRules('max_size', 10240); // 10MB default

        if (empty($answer) && $this->is_required) {
            $errors[] = 'This question is required.';
            return $errors;
        }

        if (!is_array($answer)) {
            $errors[] = 'Answer must be an array of files.';
            return $errors;
        }

        foreach ($answer as $file) {
            if (!isset($file['type']) || !in_array($file['type'], $allowedTypes)) {
                $errors[] = 'File type not allowed.';
            }

            if (!isset($file['size']) || $file['size'] > $maxSize) {
                $errors[] = 'File size exceeds maximum allowed size.';
            }
        }

        return $errors;
    }

    private function validateDate($answer): array
    {
        $errors = [];

        if (empty($answer) && $this->is_required) {
            $errors[] = 'This question is required.';
            return $errors;
        }

        if (!strtotime($answer)) {
            $errors[] = 'Invalid date format.';
        }

        return $errors;
    }

    private function validateCode($answer): array
    {
        $errors = [];
        $maxLength = $this->getValidationRules('max_length', 50000);

        if (empty($answer) && $this->is_required) {
            $errors[] = 'This question is required.';
            return $errors;
        }

        if (strlen($answer) > $maxLength) {
            $errors[] = "Code must not exceed {$maxLength} characters.";
        }

        return $errors;
    }

    private function validateNumeric($answer): array
    {
        $errors = [];
        $min = $this->getValidationRules('min');
        $max = $this->getValidationRules('max');

        if (empty($answer) && $this->is_required) {
            $errors[] = 'This question is required.';
            return $errors;
        }

        if (!is_numeric($answer)) {
            $errors[] = 'Answer must be a number.';
            return $errors;
        }

        if ($min !== null && $answer < $min) {
            $errors[] = "Answer must be at least {$min}.";
        }

        if ($max !== null && $answer > $max) {
            $errors[] = "Answer must not exceed {$max}.";
        }

        return $errors;
    }

    private function validateRating($answer): array
    {
        $errors = [];
        $min = $this->getValidationRules('min', 1);
        $max = $this->getValidationRules('max', 5);

        if (empty($answer) && $this->is_required) {
            $errors[] = 'This question is required.';
            return $errors;
        }

        if (!is_numeric($answer)) {
            $errors[] = 'Answer must be a number.';
            return $errors;
        }

        if ($answer < $min || $answer > $max) {
            $errors[] = "Answer must be between {$min} and {$max}.";
        }

        return $errors;
    }

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

        static::creating(function ($question) {
            if (empty($question->order)) {
                $maxOrder = static::where('quiz_id', $question->quiz_id)->max('order');
                $question->order = ($maxOrder ?? 0) + 1;
            }
        });
    }

    /**
     * Get the points attribute.
     */
    public function getPointsAttribute()
    {
        return $this->score_points;
    }
}