<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

final class PwPromo extends Model
{
    protected $table = 'pw_promo';

    protected $fillable = [
        'code',
        'name',
        'description',
        'discount_type',
        'discount_value',
        'min_amount',
        'max_discount',
        'usage_limit',
        'usage_count',
        'per_user_limit',
        'is_active',
        'is_auto',
        'start_date',
        'end_date',
    ];

    protected $casts = [
        'discount_value' => 'decimal:2',
        'min_amount' => 'integer',
        'max_discount' => 'integer',
        'usage_limit' => 'integer',
        'usage_count' => 'integer',
        'per_user_limit' => 'integer',
        'is_active' => 'boolean',
        'is_auto' => 'boolean',
        'start_date' => 'datetime',
        'end_date' => 'datetime',
    ];

    public function usages(): HasMany
    {
        return $this->hasMany(PwPromoUsage::class, 'promo_id');
    }

    public function scopeByCode(Builder $query, string $code): Builder
    {
        return $query->where('code', strtoupper($code));
    }

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

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

    public function scopeMinAmount(Builder $query, int $amount): Builder
    {
        return $query->where('min_amount', '<=', $amount);
    }

    public function scopeWithinPeriod(Builder $query): Builder
    {
        return $query->where(function (Builder $q): void {
            $q->whereNull('start_date')
                ->orWhere('start_date', '<=', now());
        })->where(function (Builder $q): void {
            $q->whereNull('end_date')
                ->orWhere('end_date', '>=', now());
        });
    }

    public function scopeNotUsedBy(Builder $query, int $userId): Builder
    {
        return $query->where(function (Builder $q): void {
            $q->whereNull('usage_limit')
                ->orWhereRaw('usage_count < usage_limit');
        })->where(function (Builder $q) use ($userId): void {
            $q->where('per_user_limit', 0)
                ->orWhereRaw('(SELECT COUNT(*) FROM pw_promo_usage WHERE pw_promo_usage.promo_id = pw_promo.id AND pw_promo_usage.user_id = ?) < pw_promo.per_user_limit', [$userId]);
        });
    }

    public function isValid(): bool
    {
        if (! $this->is_active) {
            return false;
        }

        $now = now();

        if ($this->start_date && $now->lt($this->start_date)) {
            return false;
        }

        if ($this->end_date && $now->gt($this->end_date)) {
            return false;
        }

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

        return true;
    }

    public function canBeUsedByUser(int $userId): bool
    {
        if (! $this->isValid()) {
            return false;
        }

        $userUsageCount = $this->usages()->where('user_id', $userId)->count();

        return $userUsageCount < $this->per_user_limit;
    }

    public function calculateDiscount(int $amount, int $goldRate = 1000): array
    {
        $discount = 0;
        $bonusGold = 0;

        if ($amount < $this->min_amount) {
            return ['discount' => 0, 'bonus_gold' => 0];
        }

        switch ($this->discount_type) {
            case 'percentage':
                $discount = (int) floor($amount * ($this->discount_value / 100));
                if ($this->max_discount && $discount > $this->max_discount) {
                    $discount = $this->max_discount;
                }
                break;
            case 'fixed':
                $discount = (int) $this->discount_value;
                break;
            case 'bonus_gold_fixed':
                $bonusGold = (int) $this->discount_value;
                break;
            case 'bonus_gold_percentage':
                $baseGold = (int) floor($amount / $goldRate);
                $bonusGold = (int) floor($baseGold * ($this->discount_value / 100));
                break;
        }

        return [
            'discount' => $discount,
            'bonus_gold' => $bonusGold,
        ];
    }
}
