<?php

declare(strict_types=1);

namespace App\Models;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;

/**
 * Subscription Plan Model.
 *
 * Defines the limits and features available for each subscription tier.
 * Plans are managed by Super Admin only.
 *
 * LIMIT CONVENTIONS:
 * - 0 = unlimited (for max_users, max_credits, etc.)
 * - Null = not applicable
 *
 * @property int $id
 * @property string $name
 * @property string $slug
 * @property string|null $description
 * @property string $price
 * @property string|null $annual_price
 * @property string $billing_cycle
 * @property string $currency_code
 * @property int $max_users
 * @property int $max_collectors
 * @property int $max_active_credits
 * @property string $max_monthly_volume
 * @property int $max_clients
 * @property array|null $features
 * @property bool $has_pwa_access
 * @property bool $is_active
 * @property bool $is_public
 * @property bool $is_trial_available
 * @property int $trial_days
 * @property int $sort_order
 * @property string|null $badge_text
 * @property string|null $badge_color
 * @property Carbon $created_at
 * @property Carbon $updated_at
 * @property Carbon|null $deleted_at
 * @property-read Collection|Subscription[] $subscriptions
 */
class Plan extends Model
{
    use HasFactory, SoftDeletes;

    protected $fillable = [
        'name',
        'slug',
        'description',
        'price',
        'annual_price',
        'billing_cycle',
        'currency_code',
        'max_users',
        'max_collectors',
        'max_active_credits',
        'max_monthly_volume',
        'max_clients',
        'features',
        'has_pwa_access',
        'is_active',
        'is_public',
        'is_trial_available',
        'trial_days',
        'sort_order',
        'badge_text',
        'badge_color',
    ];

    protected $casts = [
        'price' => 'decimal:2',
        'annual_price' => 'decimal:2',
        'max_users' => 'integer',
        'max_collectors' => 'integer',
        'max_active_credits' => 'integer',
        'max_monthly_volume' => 'decimal:2',
        'max_clients' => 'integer',
        'features' => 'array',
        'has_pwa_access' => 'boolean',
        'is_active' => 'boolean',
        'is_public' => 'boolean',
        'is_trial_available' => 'boolean',
        'trial_days' => 'integer',
        'sort_order' => 'integer',
    ];

    // ═══════════════════════════════════════════════════════════════════════════
    // RELATIONSHIPS
    // ═══════════════════════════════════════════════════════════════════════════

    /**
     * Subscriptions using this plan.
     */
    public function subscriptions(): HasMany
    {
        return $this->hasMany(Subscription::class);
    }

    // ═══════════════════════════════════════════════════════════════════════════
    // SCOPES
    // ═══════════════════════════════════════════════════════════════════════════

    /**
     * Only active plans.
     */
    public function scopeActive($query)
    {
        return $query->where('is_active', true);
    }

    /**
     * Only public plans (for pricing page).
     */
    public function scopePublic($query)
    {
        return $query->where('is_public', true);
    }

    /**
     * Order by sort_order for display.
     */
    public function scopeOrdered($query)
    {
        return $query->orderBy('sort_order');
    }

    // ═══════════════════════════════════════════════════════════════════════════
    // ACCESSORS
    // ═══════════════════════════════════════════════════════════════════════════

    /**
     * Get formatted price.
     */
    public function getFormattedPriceAttribute(): string
    {
        return '$'.number_format((float) $this->price, 0, ',', '.');
    }

    /**
     * Get formatted annual price.
     */
    public function getFormattedAnnualPriceAttribute(): ?string
    {
        if (! $this->annual_price) {
            return null;
        }

        return '$'.number_format((float) $this->annual_price, 0, ',', '.');
    }

    /**
     * Get monthly equivalent when paying annually.
     */
    public function getMonthlyEquivalentAttribute(): ?string
    {
        if (! $this->annual_price) {
            return null;
        }

        $monthly = (float) $this->annual_price / 12;

        return '$'.number_format($monthly, 0, ',', '.');
    }

    /**
     * Get annual savings percentage.
     */
    public function getAnnualSavingsPercentAttribute(): ?int
    {
        if (! $this->annual_price || ! $this->price) {
            return null;
        }

        $monthlyTotal = (float) $this->price * 12;
        $annualPrice = (float) $this->annual_price;

        if ($monthlyTotal <= 0) {
            return null;
        }

        return (int) round((($monthlyTotal - $annualPrice) / $monthlyTotal) * 100);
    }

    // ═══════════════════════════════════════════════════════════════════════════
    // LIMIT CHECKING METHODS
    // ═══════════════════════════════════════════════════════════════════════════

    /**
     * Check if a limit is unlimited (0 means unlimited).
     */
    public function isUnlimited(string $limitField): bool
    {
        $value = $this->{$limitField};

        return $value == 0 || (float) $value == 0.0;
    }

    /**
     * Check if users limit allows more.
     */
    public function allowsMoreUsers(int $currentCount): bool
    {
        return $this->isUnlimited('max_users') || $currentCount < $this->max_users;
    }

    /**
     * Check if credits limit allows more.
     */
    public function allowsMoreCredits(int $currentCount): bool
    {
        return $this->isUnlimited('max_active_credits') || $currentCount < $this->max_active_credits;
    }

    /**
     * Check if volume limit allows more.
     */
    public function allowsMoreVolume(float $currentVolume, float $additionalAmount): bool
    {
        if ($this->isUnlimited('max_monthly_volume')) {
            return true;
        }

        return ($currentVolume + $additionalAmount) <= (float) $this->max_monthly_volume;
    }

    /**
     * Check if a specific feature is enabled.
     */
    public function hasFeature(string $featureKey): bool
    {
        if (! $this->features) {
            return false;
        }

        return $this->features[$featureKey] ?? false;
    }

    /**
     * Get limit display text (handles "unlimited").
     */
    public function getLimitDisplay(string $limitField): string
    {
        $value = $this->{$limitField};

        // Check for unlimited (0 or 0.00)
        if ($this->isUnlimited($limitField)) {
            return 'Ilimitado';
        }

        if (str_contains($limitField, 'volume')) {
            return '$'.number_format((float) $value, 0, ',', '.');
        }

        return number_format((float) $value);
    }

    // ═══════════════════════════════════════════════════════════════════════════
    // STATIC HELPERS
    // ═══════════════════════════════════════════════════════════════════════════

    /**
     * Get plan by slug.
     */
    public static function findBySlug(string $slug): ?self
    {
        return static::where('slug', $slug)->first();
    }

    /**
     * Get all active public plans for pricing page.
     */
    public static function getPublicPlans()
    {
        return static::active()
            ->public()
            ->ordered()
            ->get();
    }

    /**
     * Get the default/basic plan.
     */
    public static function getDefaultPlan(): ?self
    {
        return static::findBySlug('basic');
    }

    /**
     * Plan del trial self-serve / destacado en la landing (Profesional).
     *
     * Con los 3 planes públicos, `getPublicPlans()->first()` devolvería Básico
     * (por sort_order), así que las pantallas que muestran "el plan del trial"
     * (landing CTA, formulario de registro, pantalla de bloqueo) deben usar este
     * accesor explícito. El provisioning real del trial vive en
     * TrialRegistrationService::TRIAL_PLAN_SLUG ('professional').
     */
    public static function getTrialPlan(): ?self
    {
        return static::findBySlug('professional') ?? static::getPublicPlans()->first();
    }
}
