<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Facades\Crypt;

class EventJoinToken extends Model
{
    use HasFactory;

    protected $fillable = [
        'event_id',
        'token',
        'token_hash',
        'token_encrypted',
        'token_preview',
        'label',
        'usage_limit',
        'usage_count',
        'expires_at',
        'revoked_at',
        'created_by',
        'metadata',
    ];

    protected $casts = [
        'metadata' => 'array',
        'expires_at' => 'datetime',
        'revoked_at' => 'datetime',
        'usage_limit' => 'integer',
        'usage_count' => 'integer',
    ];

    protected $hidden = [
        'token',
        'token_encrypted',
        'token_hash',
    ];

    protected $appends = [
        'token_preview',
    ];

    public function event(): BelongsTo
    {
        return $this->belongsTo(Event::class);
    }

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

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

    public function isActive(): bool
    {
        if ($this->revoked_at !== null) {
            return false;
        }

        if ($this->expires_at !== null && $this->expires_at->isPast()) {
            return false;
        }

        if ($this->usage_limit !== null && $this->usage_count >= $this->usage_limit) {
            return false;
        }

        return true;
    }

    public function getTokenAttribute(?string $value): ?string
    {
        $encrypted = $this->attributes['token_encrypted'] ?? null;

        if (! empty($encrypted)) {
            try {
                return Crypt::decryptString($encrypted);
            } catch (\Throwable $e) {
                try {
                    return Crypt::decrypt($encrypted);
                } catch (\Throwable $e) {
                    // Fall back to stored hash if both decrypt strategies fail.
                }
            }
        }

        return $value;
    }

    public function setTokenAttribute(?string $value): void
    {
        if ($value === null) {
            $this->attributes['token'] = null;
            $this->attributes['token_hash'] = null;
            $this->attributes['token_encrypted'] = null;
            $this->attributes['token_preview'] = null;

            return;
        }

        $hash = hash('sha256', $value);

        $this->attributes['token'] = $hash;
        $this->attributes['token_hash'] = $hash;
        $this->attributes['token_encrypted'] = Crypt::encryptString($value);
        $this->attributes['token_preview'] = $this->buildPreview($value);
    }

    public function getTokenPreviewAttribute(?string $value): ?string
    {
        if ($value) {
            return $value;
        }

        $token = $this->token;

        return $token ? $this->buildPreview($token) : null;
    }

    private function buildPreview(string $token): string
    {
        $length = strlen($token);

        if ($length <= 10) {
            return $token;
        }

        return substr($token, 0, 6).'…'.substr($token, -4);
    }
}
