<?php

namespace App\Models;

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

class Coupon extends Model
{
    protected $fillable = [
        'code',
        'name',
        'description',
        'type',
        'value',
        'minimum_amount',
        'max_uses',
        'uses_count',
        'max_uses_per_user',
        'starts_at',
        'expires_at',
        'is_active',
        'metadata',
    ];

    protected $casts = [
        'minimum_amount' => 'decimal:2',
        'value' => 'decimal:2',
        'starts_at' => 'datetime',
        'expires_at' => 'datetime',
        'is_active' => 'boolean',
        'metadata' => 'array',
    ];

    protected $dates = [
        'starts_at',
        'expires_at',
    ];

    /**
     * Relationships
     */
    public function usages()
    {
        return $this->hasMany(CouponUsage::class);
    }

    /**
     * Check if coupon is currently valid
     */
    public function isValid(?User $user = null): bool
    {
        // Check if coupon is active
        if (! $this->is_active) {
            return false;
        }

        // Check start date
        if ($this->starts_at && $this->starts_at->isFuture()) {
            return false;
        }

        // Check expiration date
        if ($this->expires_at && $this->expires_at->isPast()) {
            return false;
        }

        // Check usage limits
        if ($this->max_uses && $this->uses_count >= $this->max_uses) {
            return false;
        }

        // Check per-user usage limits
        if ($user && $this->max_uses_per_user) {
            $userUsageCount = $this->usages()->where('user_id', $user->id)->count();
            if ($userUsageCount >= $this->max_uses_per_user) {
                return false;
            }
        }

        return true;
    }

    /**
     * Calculate discount amount for a given subtotal
     */
    public function calculateDiscount(float $subtotal): float
    {
        // Check minimum amount requirement
        if ($this->minimum_amount && $subtotal < $this->minimum_amount) {
            return 0;
        }

        if ($this->type === 'percentage') {
            return $subtotal * ($this->value / 100);
        }

        return min($this->value, $subtotal);
    }

    /**
     * Apply coupon to a subscription
     */
    public function applyToSubscription(Subscription $subscription, float $amount, string $currency = 'USD'): CouponUsage
    {
        $discountAmount = $this->calculateDiscount($amount);

        $usage = $this->usages()->create([
            'user_id' => $subscription->user_id,
            'subscription_id' => $subscription->id,
            'discount_amount' => $discountAmount,
            'currency' => $currency,
            'used_at' => now(),
        ]);

        // Increment usage count
        $this->increment('uses_count');

        return $usage;
    }

    /**
     * Get formatted discount value
     */
    public function getFormattedDiscountAttribute(): string
    {
        if ($this->type === 'percentage') {
            return $this->value.'%';
        }

        return '$'.number_format($this->value, 2);
    }

    /**
     * Get remaining uses
     */
    public function getRemainingUsesAttribute(): ?int
    {
        if (! $this->max_uses) {
            return null;
        }

        return max(0, $this->max_uses - $this->uses_count);
    }

    /**
     * Get remaining uses for specific user
     */
    public function getRemainingUsesForUser(User $user): ?int
    {
        if (! $this->max_uses_per_user) {
            return null;
        }

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

        return max(0, $this->max_uses_per_user - $userUsageCount);
    }

    /**
     * Check if coupon is expiring soon (within 7 days)
     */
    public function isExpiringSoon(): bool
    {
        return $this->expires_at &&
               $this->expires_at->copy()->subDays(7)->isPast() &&
               $this->expires_at->isFuture();
    }

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

    /**
     * Scope: Valid for use right now
     */
    public function scopeValid(Builder $query, ?User $user = null): Builder
    {
        $query->where('is_active', true)
            ->where(function ($q) {
                $q->whereNull('starts_at')
                    ->orWhere('starts_at', '<=', now());
            })
            ->where(function ($q) {
                $q->whereNull('expires_at')
                    ->orWhere('expires_at', '>', now());
            });

        // Check global usage limits
        $query->where(function ($q) {
            $q->whereNull('max_uses')
                ->orWhereRaw('uses_count < max_uses');
        });

        return $query;
    }

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

    /**
     * Scope: Expiring soon
     */
    public function scopeExpiringSoon(Builder $query, int $days = 7): Builder
    {
        return $query->where('expires_at', '<=', now()->addDays($days))
            ->where('expires_at', '>', now());
    }

    /**
     * Scope: Search by code or name
     */
    public function scopeSearch(Builder $query, string $term): Builder
    {
        return $query->where(function ($q) use ($term) {
            $q->where('code', 'like', "%{$term}%")
                ->orWhere('name', 'like', "%{$term}%");
        });
    }

    /**
     * Find coupon by code
     */
    public static function findByCode(string $code): ?self
    {
        return static::where('code', strtoupper($code))->first();
    }

    /**
     * Boot: Automatically uppercase coupon codes
     */
    protected static function boot()
    {
        parent::boot();

        static::creating(function ($coupon) {
            $coupon->code = strtoupper($coupon->code);
        });

        static::updating(function ($coupon) {
            if ($coupon->isDirty('code')) {
                $coupon->code = strtoupper($coupon->code);
            }
        });
    }
}
