<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon;

class Subscription extends BaseModel
{
    use HasFactory;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'user_id',
        'order_id',
        'plan_id',
        'name',
        'stripe_id',
        'stripe_status',
        'stripe_price',
        'stripe_product',
        'stripe_customer',
        'quantity',
        'trial_ends_at',
        'current_period_start',
        'current_period_end',
        'ends_at',
        'cancel_at_period_end',
        'interval',
        'amount',
        'currency',
        'is_recurring',
        'metadata',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array
     */
    protected $casts = [
        'trial_ends_at' => 'datetime',
        'current_period_start' => 'datetime',
        'current_period_end' => 'datetime',
        'ends_at' => 'datetime',
        'cancel_at_period_end' => 'boolean',
        'is_recurring' => 'boolean',
        'metadata' => 'array',
    ];

    protected $hidden = [
        'stripe_price',
        'stripe_customer',
        'stripe_product',
        'stripe_id',
        'metadata'
    ];

    /**
     * Get the user that owns the subscription.
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    /**
     * Get the plan associated with the subscription.
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function plan()
    {
        return $this->belongsTo(Plan::class);
    }

    /**
     * Get the order associated with the subscription.
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function order()
    {
        return $this->belongsTo(Order::class);
    }

    /**
     * Determine if the subscription is active.
     *
     * @return bool
     */
    public function isActive()
    {
        return $this->stripe_status === 'active' || $this->stripe_status === 'trialing';
    }

    /**
     * Determine if the subscription is on trial.
     *
     * @return bool
     */
    public function onTrial()
    {
        return $this->stripe_status === 'trialing';
    }

    /**
     * Determine if the subscription is past due.
     *
     * @return bool
     */
    public function pastDue()
    {
        return $this->stripe_status === 'past_due';
    }

    /**
     * Determine if the subscription is canceled.
     *
     * @return bool
     */
    public function canceled()
    {
        return !is_null($this->ends_at);
    }

    /**
     * Determine if the subscription has ended.
     *
     * @return bool
     */
    public function ended()
    {
        if (!$this->canceled() || is_null($this->ends_at)) {
            return false;
        }

        // Safely convert ends_at to Carbon instance if it's not already
        $endsAt = $this->getDateAttribute('ends_at');
        if (!$endsAt) {
            return false;
        }

        return $endsAt->isPast();
    }

    /**
     * Determine if the subscription is set to cancel at period end.
     *
     * @return bool
     */
    public function willCancelAtPeriodEnd()
    {
        return $this->cancel_at_period_end === true;
    }

    /**
     * Determine if the subscription can be modified (not canceled or set to cancel).
     *
     * @return bool
     */
    public function canBeModified()
    {
        return $this->isActive() && !$this->willCancelAtPeriodEnd();
    }

    /**
     * Get days remaining in the current period.
     *
     * @return int
     */
    public function daysRemaining()
    {
        if (!$this->current_period_end) {
            return 0;
        }

        // Safely convert current_period_end to Carbon instance if it's not already
        $periodEnd = $this->getDateAttribute('current_period_end');
        if (!$periodEnd) {
            return 0;
        }

        return Carbon::now()->diffInDays($periodEnd, false);
    }

    /**
     * Safely get a date attribute as Carbon instance.
     * Handles cases where the attribute might be a string instead of Carbon.
     *
     * @param string $attribute
     * @return Carbon|null
     */
    protected function getDateAttribute($attribute)
    {
        $value = $this->getAttribute($attribute);
        
        if (is_null($value)) {
            return null;
        }

        // If already a Carbon instance, return it
        if ($value instanceof Carbon) {
            return $value;
        }

        // If it's a string, try to parse it
        if (is_string($value)) {
            try {
                return Carbon::parse($value);
            } catch (\Exception $e) {
                \Illuminate\Support\Facades\Log::warning("Failed to parse {$attribute} in Subscription::getDateAttribute(): " . $e->getMessage(), [
                    $attribute => $value,
                    'subscription_id' => $this->id
                ]);
                return null;
            }
        }

        // If it's neither Carbon nor string, return null
        return null;
    }
}
