<?php

declare(strict_types=1);

namespace App\Models;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Spatie\Permission\Models\Role;

/**
 * App\Models\RolePromotionStat
 *
 * @property int $id
 * @property int $user_id
 * @property int $role_promotion_id
 * @property int $role_id
 * @property int $days_added
 * @property Carbon|null $previous_expiry_date
 * @property Carbon|null $new_expiry_date
 * @property Carbon $applied_at
 * @property Carbon|null $created_at
 * @property Carbon|null $updated_at
 * @property-read User $user
 * @property-read RolePromotion $promotion
 * @property-read Role $role
 */
class RolePromotionStat extends Model
{
    protected $fillable = [
        'user_id',
        'role_promotion_id',
        'role_id',
        'days_added',
        'previous_expiry_date',
        'new_expiry_date',
        'applied_at',
    ];

    protected $casts = [
        'days_added' => 'integer',
        'previous_expiry_date' => 'datetime',
        'new_expiry_date' => 'datetime',
        'applied_at' => 'datetime',
    ];

    /**
     * Get the user that received the promotion
     *
     * @return BelongsTo<User, $this>
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    /**
     * Get the promotion that was applied
     *
     * @return BelongsTo<RolePromotion, $this>
     */
    public function promotion(): BelongsTo
    {
        return $this->belongsTo(RolePromotion::class, 'role_promotion_id');
    }

    /**
     * Get the role that was upgraded
     */
    public function role(): BelongsTo // @phpstan-ignore missingType.generics
    {
        return $this->belongsTo(Role::class);
    }

    /**
     * Scope to get stats for a specific promotion
     */
    public function scopeForPromotion(mixed $query, int $promotionId): mixed
    {
        return $query->where('role_promotion_id', $promotionId);
    }

    /**
     * Scope to get stats for a specific user
     */
    public function scopeForUser(mixed $query, int $userId): mixed
    {
        return $query->where('user_id', $userId);
    }

    /**
     * Scope to get stats for a specific role
     */
    public function scopeForRole(mixed $query, int $roleId): mixed
    {
        return $query->where('role_id', $roleId);
    }

    /**
     * Scope to get stats within a date range
     */
    public function scopeAppliedBetween(mixed $query, mixed $startDate, mixed $endDate): mixed
    {
        return $query->whereBetween('applied_at', [$startDate, $endDate]);
    }

    /**
     * Get statistics summary for a promotion
     *
     * @return array<string, mixed>
     */
    public static function getPromotionSummary(int $promotionId): array
    {
        $stats = static::forPromotion($promotionId)->get();

        return [
            'total_upgrades' => $stats->count(),
            'total_days_added' => $stats->sum('days_added'),
            'unique_users' => $stats->unique('user_id')->count(),
            'roles_affected' => $stats->unique('role_id')->count(),
            'latest_application' => $stats->max('applied_at'),
            'earliest_application' => $stats->min('applied_at'),
        ];
    }

    /**
     * Get statistics summary for a user
     *
     * @return array<string, mixed>
     */
    public static function getUserSummary(int $userId): array
    {
        $stats = static::forUser($userId)->get();

        return [
            'total_promotions_received' => $stats->count(),
            'total_days_added' => $stats->sum('days_added'),
            'promotions' => $stats->groupBy('role_promotion_id')->map(function ($group) {
                return [
                    'promotion_id' => $group->first()->role_promotion_id,
                    'promotion_name' => $group->first()->promotion->name,
                    'times_applied' => $group->count(),
                    'total_days' => $group->sum('days_added'),
                ];
            })->values()->all(),
        ];
    }

    /**
     * Record a promotion application
     */
    public static function recordPromotion(
        int $userId,
        int $promotionId,
        int $roleId,
        int $daysAdded,
        ?Carbon $previousExpiryDate = null,
        ?Carbon $newExpiryDate = null
    ): self {
        return static::create([
            'user_id' => $userId,
            'role_promotion_id' => $promotionId,
            'role_id' => $roleId,
            'days_added' => $daysAdded,
            'previous_expiry_date' => $previousExpiryDate,
            'new_expiry_date' => $newExpiryDate,
            'applied_at' => now(),
        ]);
    }
}
