<?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\Database\Eloquent\Relations\HasOne;

class Subscription extends Model
{
    use HasFactory;

    // Billing cycle constants
    const CYCLE_ONCE = 'once';
    const CYCLE_MONTHLY = 'monthly';
    const CYCLE_QUARTERLY = 'quarterly';
    const CYCLE_YEARLY = 'yearly';

    protected $fillable = [
        'user_id',
        'plan_id',
        'start_date',
        'end_date',
        'status',
        // Recurring payment fields
        'auto_renew',
        'billing_cycle',
        'payment_gateway',
        'gateway_subscription_id',
        'gateway_plan_id',
        'next_billing_date',
        'last_payment_date',
        'failed_payment_attempts',
        'cancelled_at',
        'cancellation_reason',
        // Notification tracking
        'expiring_notified_at',
        'expired_notified_at',
        'fully_deactivated_at',
    ];

    protected $casts = [
        'start_date' => 'date',
        'end_date' => 'date',
        'auto_renew' => 'boolean',
        'next_billing_date' => 'datetime',
        'last_payment_date' => 'datetime',
        'cancelled_at' => 'datetime',
        'failed_payment_attempts' => 'integer',
        'expiring_notified_at' => 'datetime',
        'expired_notified_at' => 'datetime',
        'fully_deactivated_at' => 'datetime',
    ];

    /**
     * Get the coach (user) of this subscription.
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    /**
     * Get the plan for this subscription.
     */
    public function plan(): BelongsTo
    {
        return $this->belongsTo(Plan::class);
    }

    /**
     * Get the invoice for this subscription.
     */
    public function invoice(): HasOne
    {
        return $this->hasOne(Invoice::class);
    }

    /**
     * Get payments for this subscription.
     */
    public function payments(): HasMany
    {
        return $this->hasMany(Payment::class);
    }

    /**
     * Get audit logs for this subscription.
     */
    public function auditLogs(): HasMany
    {
        return $this->hasMany(SubscriptionAuditLog::class);
    }

    /**
     * Check if subscrip is active.
     */
    public function isActive(): bool
    {
        return $this->status === 'active' && $this->end_date && $this->end_date->isFuture();
    }

    /**
     * Check if subscription is expired.
     */
    public function isExpired(): bool
    {
        return $this->end_date && $this->end_date->isPast();
    }

    /**
     * Get days remaining until expiration.
     */
    public function daysRemaining(): int
    {
        if (!$this->end_date) return 0;
        return max(0, now()->diffInDays($this->end_date, false));
    }

    /**
     * Activate the subscription.
     */
    public function activate(): void
    {
        $this->update([
            'status' => 'active',
            'start_date' => now(),
            'end_date' => now()->addDays($this->plan->duration_days),
        ]);
    }

    /**
     * Scope for active subscriptions.
     */
    public function scopeActive($query)
    {
        return $query->where('status', 'active')
            ->whereDate('end_date', '>=', now());
    }

    /**
     * Scope for pending subscriptions.
     */
    public function scopePending($query)
    {
        return $query->where('status', 'pending');
    }

    /**
     * Get status label.
     */
    public function getStatusLabelAttribute(): string
    {
        $labels = [
            'active' => 'Activo',
            'pending' => 'Pendiente',
            'expired' => 'Expirado',
            'cancelled' => 'Cancelado',
            'suspended' => 'Suspendido',
        ];

        return $labels[$this->status] ?? $this->status;
    }

    /**
     * Get status color for UI.
     */
    public function getStatusColorAttribute(): string
    {
        $colors = [
            'active' => 'green',
            'pending' => 'yellow',
            'expired' => 'gray',
            'cancelled' => 'red',
            'suspended' => 'orange',
        ];

        return $colors[$this->status] ?? 'gray';
    }

    /**
     * Check if subscription has recurring billing enabled.
     */
    public function isRecurring(): bool
    {
        return $this->auto_renew && $this->billing_cycle !== self::CYCLE_ONCE;
    }

    /**
     * Check if subscription can be renewed.
     */
    public function canRenew(): bool
    {
        return $this->auto_renew 
            && $this->payment_gateway 
            && $this->gateway_subscription_id
            && $this->failed_payment_attempts < 3;
    }

    /**
     * Check if next billing date is due.
     */
    public function isBillingDue(): bool
    {
        return $this->next_billing_date && $this->next_billing_date->isPast();
    }

    /**
     * Calculate next billing date based on cycle.
     */
    public function calculateNextBillingDate(): ?\Carbon\Carbon
    {
        $from = $this->next_billing_date ?? now();
        
        return match($this->billing_cycle) {
            self::CYCLE_MONTHLY => $from->addMonth(),
            self::CYCLE_QUARTERLY => $from->addMonths(3),
            self::CYCLE_YEARLY => $from->addYear(),
            default => null,
        };
    }

    /**
     * Record a successful payment.
     */
    public function recordPayment(): void
    {
        $this->update([
            'last_payment_date' => now(),
            'next_billing_date' => $this->calculateNextBillingDate(),
            'failed_payment_attempts' => 0,
            'end_date' => $this->calculateNextBillingDate(),
        ]);
    }

    /**
     * Record a failed payment attempt.
     */
    public function recordFailedPayment(): void
    {
        $this->increment('failed_payment_attempts');
        
        if ($this->failed_payment_attempts >= 3) {
            $this->update([
                'status' => 'suspended',
                'auto_renew' => false,
            ]);
        }
    }

    /**
     * Cancel the recurring subscription.
     */
    public function cancelRecurring(string $reason = null): void
    {
        $this->update([
            'auto_renew' => false,
            'cancelled_at' => now(),
            'cancellation_reason' => $reason,
        ]);
    }

    /**
     * Get billing cycle label.
     */
    public function getBillingCycleLabelAttribute(): string
    {
        return match($this->billing_cycle) {
            self::CYCLE_MONTHLY => 'Mensual',
            self::CYCLE_QUARTERLY => 'Trimestral',
            self::CYCLE_YEARLY => 'Anual',
            default => 'Único',
        };
    }

    /**
     * Scope for subscriptions due for billing.
     */
    public function scopeDueForBilling($query)
    {
        return $query->where('auto_renew', true)
            ->where('status', 'active')
            ->where('next_billing_date', '<=', now())
            ->where('failed_payment_attempts', '<', 3);
    }

    /**
     * Reactivate an expired subscription after successful payment.
     */
    public function reactivate(int $durationDays = null): void
    {
        $duration = $durationDays ?? $this->plan->duration_days ?? 30;

        $this->update([
            'status' => 'active',
            'start_date' => now(),
            'end_date' => now()->addDays($duration),
            'last_payment_date' => now(),
            'next_billing_date' => $this->isRecurring() ? $this->calculateNextBillingDate() : null,
            'failed_payment_attempts' => 0,
            // Reset notification flags for new cycle
            'expiring_notified_at' => null,
            'expired_notified_at' => null,
            'fully_deactivated_at' => null,
        ]);
    }

    /**
     * Check if subscription is in grace period.
     */
    public function isInGracePeriod(int $graceDays = 3): bool
    {
        if (!$this->isExpired()) {
            return false;
        }

        return $this->end_date->addDays($graceDays)->isFuture();
    }

    /**
     * Check if subscription can be reactivated.
     */
    public function canBeReactivated(): bool
    {
        return in_array($this->status, ['expired', 'suspended', 'cancelled']);
    }
}
