<?php

namespace App\Models;

use App\Support\Auth\Capabilities;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Models\Contracts\FilamentUser;
use Filament\Models\Contracts\HasDefaultTenant;
use Filament\Models\Contracts\HasTenants;
use Filament\Panel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Schema;

class User extends Authenticatable implements FilamentUser, HasDefaultTenant, HasTenants
{
    /** @use HasFactory<\Database\Factories\UserFactory> */
    use HasFactory, Notifiable;

    use SoftDeletes;

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

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

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

    public function canAccessPanel(Panel $panel): bool
    {
        return true;
    }

    public function tenants(): BelongsToMany
    {
        return $this->belongsToMany(Tenant::class, 'tenant_memberships')
            ->using(TenantMembership::class)
            ->withPivot(['id', 'role', 'source', 'source_ref', 'created_by_user_id'])
            ->withTimestamps();
    }

    public function workspaces(): BelongsToMany
    {
        return $this->belongsToMany(Workspace::class, 'workspace_memberships')
            ->using(WorkspaceMembership::class)
            ->withPivot(['id', 'role'])
            ->withTimestamps();
    }

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

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

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

    private function tenantPivotTableExists(): bool
    {
        static $exists;

        return $exists ??= Schema::hasTable('tenant_memberships');
    }

    private function tenantPreferencesTableExists(): bool
    {
        static $exists;

        return $exists ??= Schema::hasTable('user_tenant_preferences');
    }

    public function tenantRoleValue(Tenant $tenant): ?string
    {
        if (! $this->tenantPivotTableExists()) {
            return null;
        }

        $role = $this->tenants()
            ->whereKey($tenant->getKey())
            ->value('role');

        if (! is_string($role)) {
            return null;
        }

        return $role;
    }

    public function allowsTenantSync(Tenant $tenant): bool
    {
        return Gate::forUser($this)->allows(Capabilities::TENANT_SYNC, $tenant);
    }

    public function canAccessTenant(Model $tenant): bool
    {
        if (! $tenant instanceof Tenant) {
            return false;
        }

        if (! $this->tenantPivotTableExists()) {
            return false;
        }

        return $this->tenantMemberships()
            ->where('tenant_id', $tenant->getKey())
            ->exists();
    }

    public function getTenants(Panel $panel): array|Collection
    {
        if (! $this->tenantPivotTableExists()) {
            return collect();
        }

        $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId();

        return $this->tenants()
            ->when($workspaceId !== null, fn ($query) => $query->where('tenants.workspace_id', $workspaceId))
            ->where('status', 'active')
            ->orderBy('name')
            ->get();
    }

    public function getDefaultTenant(Panel $panel): ?Model
    {
        if (! $this->tenantPivotTableExists()) {
            return null;
        }

        $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId();

        $tenantId = null;

        if ($this->tenantPreferencesTableExists()) {
            $tenantId = $this->tenantPreferences()
                ->whereNotNull('last_used_at')
                ->orderByDesc('last_used_at')
                ->value('tenant_id');
        }

        if ($tenantId !== null) {
            $tenant = $this->tenants()
                ->when($workspaceId !== null, fn ($query) => $query->where('tenants.workspace_id', $workspaceId))
                ->where('status', 'active')
                ->whereKey($tenantId)
                ->first();

            if ($tenant !== null) {
                return $tenant;
            }
        }

        return $this->tenants()
            ->when($workspaceId !== null, fn ($query) => $query->where('tenants.workspace_id', $workspaceId))
            ->where('status', 'active')
            ->orderBy('name')
            ->first();
    }
}
