<?php

namespace Licenzo\Model;

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

use Licenzo\Casts\MoneyCast;
use Licenzo\Framework\Database\Model;
use Licenzo\Services\Integrations\ProviderAliasService;
use Licenzo\Traits\QueryBuilder\HasSearch;

class Subscription extends Model
{
    use HasSearch;

    protected $searchables = [
        'subscription_name'
    ];

    protected $table = 'licenzo_subscriptions';

    // Status constants
    const STATUS_PENDING = 'pending';
    const STATUS_ACTIVE = 'active';
    const STATUS_PAUSED = 'paused';
    const STATUS_CANCELED = 'canceled';
    const STATUS_EXPIRED = 'expired';

    // Billing interval constants
    const INTERVAL_DAILY = 'daily';
    const INTERVAL_WEEKLY = 'weekly';
    const INTERVAL_MONTHLY = 'monthly';
    const INTERVAL_YEARLY = 'yearly';

    // Collection method constants
    const COLLECTION_CHARGE_AUTOMATICALLY = 'charge_automatically';
    const COLLECTION_SEND_INVOICE = 'send_invoice';

    // Dunning status constants
    const DUNNING_ACTIVE = 'active';
    const DUNNING_PAUSED = 'paused';
    const DUNNING_EXHAUSTED = 'exhausted';

    protected $fillable = [
        'uuid',
        'customer_id',
        'parent_order_id',
        'provider',
        'provider_id',
        'subscription_name',
        'currency',
        'billing_cycle',
        'signup_fee',
        'initial_tax_total',
        'recurring_amount',
        'recurring_tax_total',
        'recurring_total',
        'billing_total_cycles',
        'bill_count',
        'total_paid',
        'total_refunded',
        'expire_at',
        'trial_ends_at',
        'canceled_at',
        'restored_at',
        'collection_method',
        'next_billing_date',
        'trial_days',
        'provider_customer_id',
        'provider_plan_id',
        'provider_subscription_id',
        'status',
        'amount',
        'original_plan',
        'provider_response',
        'current_payment_method',
        'config',
        'failed_payment_count',
        'last_payment_attempt',
        'next_payment_attempt',
        'dunning_status'
    ];

    protected $casts = [
        'id' => 'int',
        'customer_id' => 'int',
        'parent_order_id' => 'int',
        'provider' => 'string',
        'provider_id' => 'string',
        'currency' => 'string',
        'signup_fee' => MoneyCast::class,
        'initial_tax_total' => MoneyCast::class,
        'recurring_amount' => MoneyCast::class,
        'recurring_tax_total' => MoneyCast::class,
        'recurring_total' => MoneyCast::class,
        'billing_total_cycles' => 'int',
        'bill_count' => 'int',
        'total_paid' => MoneyCast::class,
        'total_refunded' => MoneyCast::class,
        'amount' => MoneyCast::class,
        'trial_days' => 'int',
        'failed_payment_count' => 'int',
        'expire_at' => 'datetime',
        'trial_ends_at' => 'datetime',
        'canceled_at' => 'datetime',
        'restored_at' => 'datetime',
        'next_billing_date' => 'datetime',
        'last_payment_attempt' => 'datetime',
        'next_payment_attempt' => 'datetime',
        'original_plan' => 'array',
        'provider_response' => 'array',
        'config' => 'array',
        'created_at' => 'datetime',
        'updated_at' => 'datetime'
    ];

    // Relationships
    public function customer()
    {
        return $this->belongsTo(Customer::class);
    }

    public function licenses()
    {
        return $this->hasMany(License::class);
    }

    public function subscriptionProducts()
    {
        return $this->hasMany(SubscriptionProduct::class);
    }

    public function meta()
    {
        return $this->hasMany(SubscriptionMeta::class, 'entity_id')
            ->where('entity_type', 'subscription');
    }

    public function transactions()
    {
        return $this->hasMany(SubscriptionTransaction::class);
    }

    public function events()
    {
        return $this->hasMany(SubscriptionEvent::class);
    }

    public function schedules()
    {
        return $this->hasMany(SubscriptionSchedule::class);
    }

    public function invoices()
    {
        return $this->hasMany(Invoice::class);
    }

    public function idempotencyKeys()
    {
        return $this->hasMany(IdempotencyKey::class);
    }

    // Scopes
    public function scopeActive($query)
    {
        return $query->where('status', static::STATUS_ACTIVE);
    }

    public function scopePending($query)
    {
        return $query->where('status', static::STATUS_PENDING);
    }

    public function scopePaused($query)
    {
        return $query->where('status', static::STATUS_PAUSED);
    }

    public function scopeCanceled($query)
    {
        return $query->where('status', static::STATUS_CANCELED);
    }

    public function scopeExpired($query)
    {
        return $query->where('status', static::STATUS_EXPIRED);
    }

    public function scopeDueForRenewal($query)
    {
        return $query->where('status', static::STATUS_ACTIVE)
            ->where('next_billing_date', '<=', gmdate('Y-m-d H:i:s'));
    }

    public function scopeOnTrial($query)
    {
        return $query->where('status', static::STATUS_ACTIVE)
            ->whereNotNull('trial_ends_at')
            ->where('trial_ends_at', '>', gmdate('Y-m-d H:i:s'));
    }

    // Helpers
    public function isActive(): bool
    {
        return $this->status === static::STATUS_ACTIVE;
    }

    public function isPending(): bool
    {
        return $this->status === static::STATUS_PENDING;
    }

    public function isPaused(): bool
    {
        return $this->status === static::STATUS_PAUSED;
    }

    public function isCanceled(): bool
    {
        return $this->status === static::STATUS_CANCELED;
    }

    public function isExpired(): bool
    {
        return $this->status === static::STATUS_EXPIRED;
    }

    public function isOnTrial(): bool
    {
        return $this->trial_ends_at && $this->trial_ends_at->isFuture();
    }

    public function canBill(): bool
    {
        if (!$this->isActive()) return false;
        if ($this->billing_total_cycles > 0 && $this->bill_count >= $this->billing_total_cycles) return false;
        return true;
    }

    public function getBillingCycleAttribute($value)
    {
        return ($value === null || $value === '') ? static::INTERVAL_MONTHLY : (string) $value;
    }

    public function setProviderAttribute($value)
    {
        $this->attributes['provider'] = ProviderAliasService::normalize((string) $value, SubscriptionTransaction::PROVIDER_MANUAL);
    }

    public function setBillingCycleAttribute($value)
    {
        $normalized = strtolower(trim((string) ($value ?? '')));
        $allowed = [
            ProductVariation::PRICE_CYCLE_ONETIME,
            ProductVariation::PRICE_CYCLE_WEEKLY,
            static::INTERVAL_MONTHLY,
            static::INTERVAL_YEARLY,
            ProductVariation::PRICE_CYCLE_LIFETIME,
            'split',
        ];

        $this->attributes['billing_cycle'] = in_array($normalized, $allowed, true)
            ? $normalized
            : static::INTERVAL_MONTHLY;
    }

    public function getBillingTotalCyclesAttribute($value)
    {
        return (int) ($value ?? 0);
    }

    public function setBillingTotalCyclesAttribute($value)
    {
        $this->attributes['billing_total_cycles'] = max(0, (int) ($value ?? 0));
    }

    public function isDueForRenewal(): bool
    {
        return $this->isActive() &&
            $this->next_billing_date &&
            $this->next_billing_date->isPast();
    }

    public function hasFailedPayments(): bool
    {
        return $this->failed_payment_count > 0;
    }

    public function isInDunning(): bool
    {
        return !empty($this->dunning_status) && $this->dunning_status !== static::DUNNING_EXHAUSTED;
    }

    public function hasPendingSchedules(): bool
    {
        return $this->schedules()->where('status', SubscriptionSchedule::STATUS_PENDING)->exists();
    }

    public function hasPendingCancellation(): bool
    {
        return $this->schedules()
            ->where('status', SubscriptionSchedule::STATUS_PENDING)
            ->where('action_type', SubscriptionSchedule::ACTION_CANCEL)
            ->exists();
    }

    public function getPendingCancellation()
    {
        return $this->schedules()
            ->where('status', SubscriptionSchedule::STATUS_PENDING)
            ->where('action_type', SubscriptionSchedule::ACTION_CANCEL)
            ->first();
    }

    // Revenue calculation methods

    /**
     * Calculate total paid amount from successful transactions
     *
     * @return float
     */
    public function calculateTotalPaid(): float
    {
        $cents = (int) $this->transactions()
            ->where('status', SubscriptionTransaction::STATUS_SUCCESS)
            ->whereIn('transaction_type', [
                SubscriptionTransaction::TYPE_CHARGE,
                SubscriptionTransaction::TYPE_PURCHASE,
                SubscriptionTransaction::TYPE_RENEWAL,
                SubscriptionTransaction::TYPE_UPGRADE,
            ])
            ->sum('total');

        $factor = (int) pow(10, MoneyCast::minorUnits($this->currency ?? 'USD'));

        return $cents / $factor;
    }

    /**
     * Calculate total refunded amount
     *
     * @return float
     */
    public function calculateTotalRefunded(): float
    {
        $cents = (int) $this->transactions()
            ->where('status', SubscriptionTransaction::STATUS_SUCCESS)
            ->where('transaction_type', SubscriptionTransaction::TYPE_REFUND)
            ->sum('total');

        $factor = (int) pow(10, MoneyCast::minorUnits($this->currency ?? 'USD'));

        return $cents / $factor;
    }

    /**
     * Calculate net revenue (total paid - total refunded)
     *
     * @return float
     */
    public function calculateNetRevenue(): float
    {
        return $this->calculateTotalPaid() - $this->calculateTotalRefunded();
    }

    /**
     * Sync revenue fields from transactions
     * Recalculates and updates total_paid and total_refunded
     *
     * @return bool
     */
    public function syncRevenue(): bool
    {
        return $this->update([
            'total_paid' => $this->calculateTotalPaid(),
            'total_refunded' => $this->calculateTotalRefunded(),
        ]);
    }

    /**
     * Get net revenue (accessor)
     *
     * @return float
     */
    public function getNetRevenueAttribute(): float
    {
        return ($this->total_paid ?? 0) - ($this->total_refunded ?? 0);
    }

    // Scopes for revenue-based queries

    /**
     * Scope to filter high-value subscriptions
     *
     * @param \Licenzo\Framework\Database\Eloquent\Builder $query
     * @param float $threshold
     * @return \Licenzo\Framework\Database\Eloquent\Builder
     */
    public function scopeHighValue($query, float $threshold = 1000, string $currency = 'USD')
    {
        $factor = (int) pow(10, MoneyCast::minorUnits($currency));
        $thresholdCents = (int) round($threshold * $factor);

        return $query->whereRaw('(total_paid - total_refunded) >= ?', [$thresholdCents]);
    }

    /**
     * Scope to order by net revenue
     *
     * @param \Licenzo\Framework\Database\Eloquent\Builder $query
     * @param string $direction
     * @return \Licenzo\Framework\Database\Eloquent\Builder
     */
    public function scopeOrderByRevenue($query, string $direction = 'desc')
    {
        return $query->orderByRaw('(total_paid - total_refunded) ' . $direction);
    }

    /**
     * Scope to get top revenue subscriptions
     *
     * @param \Licenzo\Framework\Database\Eloquent\Builder $query
     * @param int $limit
     * @return \Licenzo\Framework\Database\Eloquent\Builder
     */
    public function scopeTopRevenue($query, int $limit = 100)
    {
        return $query->orderByRevenue('desc')->limit($limit);
    }
}
