<?php

namespace App\Models;

use Filament\Models\Contracts\FilamentUser;
use Filament\Panel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Cache;
use Laravel\Sanctum\HasApiTokens;
use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable implements FilamentUser
{
    use HasApiTokens, HasFactory, HasRoles, Notifiable;

    protected $with = ['company'];

    // Los roles se gestionan exclusivamente via Spatie Permission (assignRole / hasRole /
    // getRoleNames). No existe columna `role` en la tabla users — model_has_roles es la
    // única fuente de verdad. No añadir 'role' aquí: causaría un error SQL oscuro al
    // intentar escribir una columna inexistente.
    protected $fillable = [
        'name',
        'email',
        'password',
        'company_id',
        'phone',
        'sees_all_collectors',
    ];

    protected $hidden = [
        'password',
        'remember_token',
    ];

    protected $casts = [
        'email_verified_at' => 'datetime',
        'password' => 'hashed',
        'sees_all_collectors' => 'boolean',
    ];

    // Relación con la compañía (Tenant)
    public function company()
    {
        return $this->belongsTo(Company::class);
    }

    // ------------------------------------------------------------------
    // Relaciones supervisor ↔ collector
    // ------------------------------------------------------------------

    /**
     * Collectors asignados a este supervisor (o admin).
     *
     * Uso: $supervisor->assignedCollectors()->pluck('users.id')
     * Asignación: $supervisor->assignedCollectors()->attach($collectorId, ['company_id' => $companyId])
     */
    public function assignedCollectors(): BelongsToMany
    {
        return $this->belongsToMany(
            User::class,
            'supervisor_collector',
            'supervisor_id',
            'collector_id'
        )
            ->using(SupervisorCollector::class)
            ->withPivot('company_id')
            ->withTimestamps();
    }

    /**
     * Supervisores (o admins) que tienen asignado a este collector.
     *
     * Uso: $collector->assignedSupervisors
     */
    public function assignedSupervisors(): BelongsToMany
    {
        return $this->belongsToMany(
            User::class,
            'supervisor_collector',
            'collector_id',
            'supervisor_id'
        )
            ->using(SupervisorCollector::class)
            ->withPivot('company_id')
            ->withTimestamps();
    }

    // ------------------------------------------------------------------
    // Lógica de Roles y Suscripción
    // ------------------------------------------------------------------

    /**
     * Helper para verificar si el usuario es el Super Administrador global.
     */
    public function isSuperAdmin(): bool
    {
        // El Super Admin global debe tener company_id NULL y el rol específico.
        return $this->company_id === null && $this->hasRole('super_admin');
    }

    /**
     * Verifica si la compañía del usuario tiene una suscripción activa y vigente.
     */
    public function hasActiveSubscription(): bool
    {
        // El Super Admin (quien no tiene company_id asociado) siempre tiene acceso.
        if ($this->isSuperAdmin()) {
            return true;
        }

        // Si el usuario NO es Super Admin y no tiene una compañía asociada, denegar.
        // Esto captura usuarios con company_id != NULL que no son Super Admin.
        if (! $this->company_id || ! $this->company) {
            return false;
        }

        // Verifica si la compañía tiene al menos una suscripción activa y que no haya expirado.
        //
        // Cacheado por company_id: EnsurePwaAccess invoca este método en CADA request PWA,
        // y antes ejecutaba un EXISTS contra `subscriptions` por petición. La suscripción
        // cambia con muy poca frecuencia (acción admin o el job nocturno
        // subscriptions:update-status), así que un TTL corto es seguro. La key se invalida
        // explícitamente cuando una Subscription se guarda/elimina (ver Subscription::booted),
        // de modo que una renovación se refleja de inmediato sin esperar al TTL.
        return Cache::remember(
            self::subscriptionCacheKey($this->company_id),
            now()->addMinutes(5),
            // active() = status IN (active, trial, grace); current() = dentro de
            // término o de período de gracia. Basado en `status` (no solo
            // is_active) para que el período de gracia conceda acceso, coherente
            // con Subscription::isActive()/isInGracePeriod() y currentForCompany().
            fn (): bool => $this->company->subscriptions()
                ->active()
                ->current()
                ->exists(),
        );
    }

    /**
     * Cache key para el estado de suscripción de una company.
     *
     * Centralizado para que Subscription::booted() pueda invalidarlo con la misma clave.
     */
    public static function subscriptionCacheKey(int $companyId): string
    {
        return "subscription_active:{$companyId}";
    }

    // ------------------------------------------------------------------
    // LÓGICA DE ACCESO DE FILAMENT (Implementación de FilamentUser)
    // ------------------------------------------------------------------

    /**
     * Determina si el usuario puede acceder al Panel de Administración de Filament.
     * Esta función se ejecuta después del login exitoso.
     */
    public function canAccessPanel(Panel $panel): bool
    {
        // El panel /admin es solo para super_admin y admin de empresa; los
        // supervisores, cobradores y clientes usan la PWA. (#124: sin el chequeo de
        // rol era default-allow, cualquier rol con suscripción entraba.)
        //
        // El gate de SUSCRIPCIÓN vive en el middleware EnsureActiveSubscription, no
        // aquí: la prioridad de middleware de Laravel obliga a Authenticate (que llama
        // a este método) a correr antes que cualquier middleware propio, así que un
        // chequeo de suscripción aquí produciría un 403 seco imposible de interceptar.
        // El middleware corre tras Authenticate y redirige a la pantalla de
        // reactivación /suscripcion. Se aplica a TODAS las rutas del panel, así que el
        // bloqueo por suscripción expirada sigue siendo hermético (Fase 3).
        return $this->hasAnyRole(['super_admin', 'admin']);
    }
}
