<?php

namespace App\Models;

use App\enum\UserLevel;
use Database\Factories\UserFactory;
use Filament\Models\Contracts\FilamentUser;
use Filament\Panel;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Str;
use Laravel\Cashier\Billable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable implements FilamentUser, MustVerifyEmail
{
    /** @use HasFactory<UserFactory> */
    use Billable, HasApiTokens, HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
        'level',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var list<string>
     */
    protected $hidden = [
        'password',
        'remember_token',
        'level',
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
            'level' => UserLevel::class,
        ];
    }

    public function initials(): string
    {
        return Str::of($this->name)
            ->explode(' ')
            ->map(fn (string $name) => Str::of($name)->substr(0, 1))
            ->implode('');
    }

    public function canAccessPanel(Panel $panel): bool
    {
        return str_ends_with($this->email, '@zemail.me') && $this->level === UserLevel::SUPERADMIN && $this->hasVerifiedEmail();
    }

    /**
     * Scope to query only super admin users.
     */
    public function scopeIsSuperAdmin($query)
    {
        return $query->where('level', UserLevel::SUPERADMIN->value);
    }

    /**
     * Scope to query only normal users.
     */
    public function scopeIsNormalUser($query)
    {
        return $query->where('level', UserLevel::NORMALUSER->value);
    }

    /**
     * Scope to query only banner users.
     */
    public function scopeIsBannerUser($query)
    {
        return $query->where('level', UserLevel::BANNEDUSER->value);
    }

    /**
     * Check if user is a super admin.
     */
    public function isSuperAdmin(): bool
    {
        return $this->level === UserLevel::SUPERADMIN;
    }

    /**
     * Check if user is a normal user.
     */
    public function isNormalUser(): bool
    {
        return $this->level === UserLevel::NORMALUSER;
    }

    /**
     * Check if user is a banner user.
     */
    public function isBannerUser(): bool
    {
        return $this->level === UserLevel::BANNEDUSER;
    }

    /**
     * Get user level name.
     */
    public function getLevelName(): string
    {
        return $this->level->name;
    }

    public function tickets(): HasMany
    {
        return $this->hasMany(Ticket::class);
    }

    public function logs()
    {
        return $this->hasMany(Log::class);
    }

    public function usageLogs()
    {
        return $this->hasMany(UsageLog::class);
    }

    public function impersonationLogs()
    {
        return $this->hasMany(ImpersonationLog::class, 'admin_id');
    }

    public function impersonationTargets()
    {
        return $this->hasMany(ImpersonationLog::class, 'target_user_id');
    }

    /**
     * Get all subscriptions for the user
     */
    public function subscriptions()
    {
        return $this->hasMany(Subscription::class);
    }

    /**
     * Get the current active subscription for the user
     */
    public function currentSubscription()
    {
        return $this->hasOne(Subscription::class)
            ->where(function ($query) {
                $query->where('status', 'active')
                    ->orWhere('status', 'trialing');
            })
            ->where(function ($query) {
                $query->whereNull('ends_at')
                    ->orWhere('ends_at', '>', now());
            })
            ->latest();
    }

    /**
     * Get the latest subscription (regardless of status)
     */
    public function latestSubscription()
    {
        return $this->hasOne(Subscription::class)->latestOfMany();
    }

    /**
     * Scope: Users with active subscriptions
     */
    public function scopeWithActiveSubscription($query)
    {
        return $query->whereHas('subscriptions', function ($subscriptionQuery) {
            $subscriptionQuery->where(function ($q) {
                $q->where('status', 'active')
                    ->orWhere('status', 'trialing');
            })->where(function ($q) {
                $q->whereNull('ends_at')
                    ->orWhere('ends_at', '>', now());
            });
        });
    }

    /**
     * Scope: Users with trial subscriptions
     */
    public function scopeWithTrialSubscription($query)
    {
        return $query->whereHas('subscriptions', function ($subscriptionQuery) {
            $subscriptionQuery->where('status', 'trialing')
                ->where('trial_ends_at', '>', now());
        });
    }

    /**
     * Scope: Users with cancelled subscriptions
     */
    public function scopeWithCancelledSubscription($query)
    {
        return $query->whereHas('subscriptions', function ($subscriptionQuery) {
            $subscriptionQuery->where('status', 'cancelled')
                ->orWhere(function ($q) {
                    $q->where('ends_at', '<=', now());
                });
        });
    }

    /**
     * Scope: Users without any active subscriptions
     */
    public function scopeWithoutActiveSubscription($query)
    {
        return $query->whereDoesntHave('subscriptions', function ($subscriptionQuery) {
            $subscriptionQuery->where(function ($q) {
                $q->where('status', 'active')
                    ->orWhere('status', 'trialing');
            })->where(function ($q) {
                $q->whereNull('ends_at')
                    ->orWhere('ends_at', '>', now());
            });
        });
    }

    /**
     * Scope: Users by subscription provider
     */
    public function scopeBySubscriptionProvider($query, string $provider)
    {
        return $query->whereHas('subscriptions', function ($subscriptionQuery) use ($provider) {
            $subscriptionQuery->where('provider', $provider)
                ->where(function ($q) {
                    $q->whereNull('ends_at')
                        ->orWhere('ends_at', '>', now());
                });
        });
    }

    /**
     * Scope: Users with subscriptions expiring soon (within given days)
     */
    public function scopeWithSubscriptionExpiringSoon($query, int $days = 7)
    {
        return $query->whereHas('subscriptions', function ($subscriptionQuery) use ($days) {
            $subscriptionQuery->where('status', 'active')
                ->whereNotNull('ends_at')
                ->where('ends_at', '<=', now()->addDays($days))
                ->where('ends_at', '>', now());
        });
    }

    /**
     * Check if user has an active subscription
     */
    public function hasActiveSubscription(): bool
    {
        return $this->subscriptions()
            ->where(function ($query) {
                $query->where('status', 'active')
                    ->orWhere('status', 'trialing');
            })
            ->where(function ($query) {
                $query->whereNull('ends_at')
                    ->orWhere('ends_at', '>', now());
            })
            ->exists();
    }

    /**
     * Check if user is currently on trial
     */
    public function isOnTrial(): bool
    {
        return $this->subscriptions()
            ->where('status', 'trialing')
            ->where('trial_ends_at', '>', now())
            ->exists();
    }

    /**
     * Check if user has cancelled subscription
     */
    public function hasCancelledSubscription(): bool
    {
        return $this->subscriptions()
            ->where(function ($query) {
                $query->where('status', 'cancelled')
                    ->orWhere(function ($q) {
                        $q->whereNotNull('ends_at')
                            ->where('ends_at', '<=', now());
                    });
            })
            ->exists();
    }

    /**
     * Check if user has ever had a subscription
     */
    public function hasHadSubscription(): bool
    {
        return $this->subscriptions()->exists();
    }

    /**
     * Get user's subscription status as string
     */
    public function getSubscriptionStatus(): string
    {
        if ($this->isOnTrial()) {
            return 'trialing';
        }

        if ($this->hasActiveSubscription()) {
            return 'active';
        }

        if ($this->hasCancelledSubscription()) {
            return 'cancelled';
        }

        return 'none';
    }

    /**
     * Get user's current subscription plan
     */
    public function getCurrentPlan(): ?Plan
    {
        return $this->currentSubscription?->plan;
    }

    /**
     * Get user's subscription expiry date
     */
    public function getSubscriptionExpiryDate(): ?\Carbon\Carbon
    {
        return $this->currentSubscription?->ends_at;
    }

    /**
     * Get user's trial end date
     */
    public function getTrialEndDate(): ?\Carbon\Carbon
    {
        $trialSubscription = $this->subscriptions()
            ->where('status', 'trialing')
            ->where('trial_ends_at', '>', now())
            ->first();

        return $trialSubscription?->trial_ends_at;
    }

    /**
     * Check if user's subscription is expiring soon (within given days)
     */
    public function isSubscriptionExpiringSoon(int $days = 7): bool
    {
        $currentSubscription = $this->currentSubscription;

        return $currentSubscription &&
               $currentSubscription->ends_at &&
               $currentSubscription->ends_at->lte(now()->addDays($days)) &&
               $currentSubscription->ends_at->gt(now());
    }

    /**
     * Get total amount spent by user across all subscriptions
     */
    public function getTotalSpent(): float
    {
        return $this->subscriptions()
            ->with('plan')
            ->get()
            ->sum(function ($subscription) {
                return $subscription->plan ? $subscription->plan->price : 0;
            });
    }

    /**
     * Get user's subscription provider
     */
    public function getSubscriptionProvider(): ?string
    {
        return $this->currentSubscription?->provider;
    }

    /**
     * Check if user can upgrade/downgrade their plan
     */
    public function canChangePlan(): bool
    {
        return $this->hasActiveSubscription() &&
               $this->currentSubscription?->isRecurring();
    }

    /**
     * Get subscription metrics for analytics
     */
    public function getSubscriptionMetrics(): array
    {
        $subscriptions = $this->subscriptions()->with('plan')->get();

        return [
            'total_subscriptions' => $subscriptions->count(),
            'active_subscriptions' => $subscriptions->where(function ($sub) {
                return in_array($sub->status, ['active', 'trialing']) &&
                       (! $sub->ends_at || $sub->ends_at->isFuture());
            })->count(),
            'total_spent' => $this->getTotalSpent(),
            'current_plan' => $this->getCurrentPlan()?->name,
            'provider' => $this->getSubscriptionProvider(),
            'status' => $this->getSubscriptionStatus(),
            'trial_ends_at' => $this->getTrialEndDate(),
            'subscription_ends_at' => $this->getSubscriptionExpiryDate(),
            'is_expiring_soon' => $this->isSubscriptionExpiringSoon(),
        ];
    }
}
