<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Database\Factories\UserFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class Usuario extends Authenticatable
{
    /** @use HasFactory<UserFactory> */
    use HasFactory, Notifiable, SoftDeletes;

    public const ROLE_ADMINISTRADOR = 'administrador';

    public const ROLE_SUPERUSUARIO = 'superusuario';

    public const ROLE_ALMACENISTA = 'almacenista';

    public const ROLE_SUPERVISOR = 'supervisor';

    public const ROLE_DESPACHADOR = 'despachador';

    public const ROLE_GESTOR = 'gestor';

    public const ROLE_SOLICITANTE = 'solicitante';

    public const ROLE_LABELS = [
        self::ROLE_ADMINISTRADOR => 'Administrador',
        self::ROLE_SUPERUSUARIO => 'Superusuario',
        self::ROLE_ALMACENISTA => 'Almacenista',
        self::ROLE_SUPERVISOR => 'Supervisor',
        self::ROLE_DESPACHADOR => 'Despachador',
        self::ROLE_GESTOR => 'Gestor',
        self::ROLE_SOLICITANTE => 'Solicitante',
    ];

    public const ROLE_TYPE_MAP = [
        self::ROLE_ADMINISTRADOR => 1,
        self::ROLE_SUPERUSUARIO => 8,
        self::ROLE_ALMACENISTA => 2,
        self::ROLE_SUPERVISOR => 5,
        self::ROLE_DESPACHADOR => 6,
        self::ROLE_GESTOR => 3,
        self::ROLE_SOLICITANTE => 7,
    ];

    public const STORAGE_LEVEL_ROLES = [
        self::ROLE_ALMACENISTA,
        self::ROLE_SUPERVISOR,
        self::ROLE_DESPACHADOR,
    ];

    public const TYPE_ROLE_MAP = [
        1 => self::ROLE_ADMINISTRADOR,
        2 => self::ROLE_ALMACENISTA,
        3 => self::ROLE_GESTOR,
        4 => self::ROLE_ALMACENISTA,
        5 => self::ROLE_SUPERVISOR,
        6 => self::ROLE_DESPACHADOR,
        7 => self::ROLE_SOLICITANTE,
        8 => self::ROLE_SUPERUSUARIO,
    ];

    protected $table = 'users';

    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
        'clave',
        'codigo',
        'cedula',
        'nombre',
        'tipo',
        'cargo',
        'dependencia',
        'ficha',
        'fechaIngreso',
        'fechaCreacion',
        'fechaActualizacion',
        'cedulaEditor',
        'tallaCamisa',
        'tallaChaqueta',
        'tallaPantalon',
        'tallaBotas',
        'tallaZapatos',
    ];

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

    protected static function booted(): void
    {
        static::saving(function (self $user): void {
            $roleKey = self::resolveRoleKey($user->cargo, $user->tipo);

            if ($roleKey === null) {
                return;
            }

            $user->cargo = self::roleLabelFromKey($roleKey);
            $user->tipo = self::roleTypeFromKey($roleKey);
        });
    }

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

    public function tallaUniforme(): HasOne
    {
        return $this->hasOne(TallaUniforme::class, 'cedula_usuario', 'cedula');
    }

    public function preciosPorBien(): HasMany
    {
        return $this->hasMany(PreciosPorBien::class, 'cedula_usuario', 'cedula');
    }

    /**
     * @return array<int, string>
     */
    public static function roleOptions(): array
    {
        return [
            self::ROLE_TYPE_MAP[self::ROLE_SUPERUSUARIO] => self::ROLE_LABELS[self::ROLE_SUPERUSUARIO],
            self::ROLE_TYPE_MAP[self::ROLE_ADMINISTRADOR] => self::ROLE_LABELS[self::ROLE_ADMINISTRADOR],
            self::ROLE_TYPE_MAP[self::ROLE_ALMACENISTA] => self::ROLE_LABELS[self::ROLE_ALMACENISTA],
            self::ROLE_TYPE_MAP[self::ROLE_SUPERVISOR] => self::ROLE_LABELS[self::ROLE_SUPERVISOR],
            self::ROLE_TYPE_MAP[self::ROLE_DESPACHADOR] => self::ROLE_LABELS[self::ROLE_DESPACHADOR],
            self::ROLE_TYPE_MAP[self::ROLE_GESTOR] => self::ROLE_LABELS[self::ROLE_GESTOR],
            self::ROLE_TYPE_MAP[self::ROLE_SOLICITANTE] => self::ROLE_LABELS[self::ROLE_SOLICITANTE],
        ];
    }

    /**
     * @return array<string, string>
     */
    public static function cargoOptions(): array
    {
        return [
            self::ROLE_LABELS[self::ROLE_SUPERUSUARIO] => self::ROLE_LABELS[self::ROLE_SUPERUSUARIO],
            self::ROLE_LABELS[self::ROLE_ADMINISTRADOR] => self::ROLE_LABELS[self::ROLE_ADMINISTRADOR],
            self::ROLE_LABELS[self::ROLE_ALMACENISTA] => self::ROLE_LABELS[self::ROLE_ALMACENISTA],
            self::ROLE_LABELS[self::ROLE_SUPERVISOR] => self::ROLE_LABELS[self::ROLE_SUPERVISOR],
            self::ROLE_LABELS[self::ROLE_DESPACHADOR] => self::ROLE_LABELS[self::ROLE_DESPACHADOR],
            self::ROLE_LABELS[self::ROLE_GESTOR] => self::ROLE_LABELS[self::ROLE_GESTOR],
            self::ROLE_LABELS[self::ROLE_SOLICITANTE] => self::ROLE_LABELS[self::ROLE_SOLICITANTE],
        ];
    }

    public static function normalizeRoleKey(?string $value): ?string
    {
        $normalized = trim((string) $value);
        if ($normalized === '') {
            return null;
        }

        $normalized = mb_strtolower($normalized, 'UTF-8');
        $normalized = strtr($normalized, [
            'á' => 'a',
            'é' => 'e',
            'í' => 'i',
            'ó' => 'o',
            'ú' => 'u',
            'ñ' => 'n',
        ]);
        $normalized = preg_replace('/[^a-z0-9]+/', ' ', $normalized) ?? '';
        $normalized = trim($normalized);

        if ($normalized === '') {
            return null;
        }

        if (str_contains($normalized, 'admin')) {
            return self::ROLE_ADMINISTRADOR;
        }

        if (str_contains($normalized, 'superusuario') || str_contains($normalized, 'super usuario')) {
            return self::ROLE_SUPERUSUARIO;
        }

        if (str_contains($normalized, 'jefe de almacen')) {
            return self::ROLE_ADMINISTRADOR;
        }

        if (str_contains($normalized, 'supervisor')) {
            return self::ROLE_SUPERVISOR;
        }

        if (str_contains($normalized, 'despach')) {
            return self::ROLE_DESPACHADOR;
        }

        if (str_contains($normalized, 'almacen')) {
            return self::ROLE_ALMACENISTA;
        }

        if (str_contains($normalized, 'operador')) {
            return self::ROLE_ALMACENISTA;
        }

        if (str_contains($normalized, 'gestor')) {
            return self::ROLE_GESTOR;
        }

        if (str_contains($normalized, 'solicit')) {
            return self::ROLE_SOLICITANTE;
        }

        if ($normalized === 'jefe') {
            return self::ROLE_GESTOR;
        }

        return match ($normalized) {
            self::ROLE_SUPERUSUARIO, 'superusuarios' => self::ROLE_SUPERUSUARIO,
            self::ROLE_ADMINISTRADOR => self::ROLE_ADMINISTRADOR,
            self::ROLE_ALMACENISTA, 'almacenistas' => self::ROLE_ALMACENISTA,
            self::ROLE_SUPERVISOR, 'supervisores' => self::ROLE_SUPERVISOR,
            self::ROLE_DESPACHADOR, 'despachadores' => self::ROLE_DESPACHADOR,
            self::ROLE_GESTOR, 'gestores' => self::ROLE_GESTOR,
            self::ROLE_SOLICITANTE, 'solicitantes' => self::ROLE_SOLICITANTE,
            default => null,
        };
    }

    /**
     * @return array<int, string>
     */
    public static function equivalentRoleKeys(string $roleKey): array
    {
        return in_array($roleKey, self::STORAGE_LEVEL_ROLES, true)
            ? self::STORAGE_LEVEL_ROLES
            : [$roleKey];
    }

    public static function resolveRoleKey(?string $cargo, int|string|null $tipo): ?string
    {
        $roleKey = self::normalizeRoleKey($cargo);

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

        $tipoValue = (int) ($tipo ?? 0);

        return self::TYPE_ROLE_MAP[$tipoValue] ?? null;
    }

    public static function roleLabelFromKey(string $roleKey): string
    {
        return self::ROLE_LABELS[$roleKey] ?? 'Sin tipo';
    }

    public static function roleTypeFromKey(string $roleKey): int
    {
        return self::ROLE_TYPE_MAP[$roleKey] ?? 0;
    }

    public function roleKey(): ?string
    {
        return self::resolveRoleKey($this->cargo, $this->tipo);
    }

    public function roleLabel(): string
    {
        $roleKey = $this->roleKey();

        return $roleKey !== null
            ? self::roleLabelFromKey($roleKey)
            : 'Sin tipo';
    }

    public function hasRole(string $roleKey): bool
    {
        return $this->roleKey() === $roleKey;
    }

    /**
     * @param array<int, string> $roleKeys
     */
    public function hasAnyRole(array $roleKeys): bool
    {
        $currentRole = $this->roleKey();

        if ($currentRole === null) {
            return false;
        }

        if ($currentRole === self::ROLE_SUPERUSUARIO) {
            return true;
        }

        $expandedRoleKeys = [];
        foreach ($roleKeys as $roleKey) {
            $expandedRoleKeys = array_merge($expandedRoleKeys, self::equivalentRoleKeys($roleKey));
        }

        return in_array($currentRole, array_values(array_unique($expandedRoleKeys)), true);
    }

    public function isAdmin(): bool
    {
        return $this->hasAnyRole([self::ROLE_ADMINISTRADOR]);
    }

    public function isSuperusuario(): bool
    {
        return $this->hasRole(self::ROLE_SUPERUSUARIO);
    }

    public function isAlmacenista(): bool
    {
        return $this->hasAnyRole([self::ROLE_ALMACENISTA]);
    }

    public function isSupervisor(): bool
    {
        return $this->hasRole(self::ROLE_SUPERVISOR);
    }

    public function isDespachador(): bool
    {
        return $this->hasRole(self::ROLE_DESPACHADOR);
    }

    public function isGestor(): bool
    {
        return $this->hasRole(self::ROLE_GESTOR);
    }

    public function isSolicitante(): bool
    {
        return $this->hasRole(self::ROLE_SOLICITANTE);
    }

    public function canAccessAdministracion(): bool
    {
        return $this->hasAnyRole([
            self::ROLE_ADMINISTRADOR,
            self::ROLE_ALMACENISTA,
            self::ROLE_GESTOR,
        ]);
    }

    public function canAccessAlmacen(): bool
    {
        return $this->hasAnyRole([
            self::ROLE_ADMINISTRADOR,
            self::ROLE_ALMACENISTA,
        ]);
    }

    public function canAccessAsignacion(): bool
    {
        return $this->hasAnyRole([
            self::ROLE_ADMINISTRADOR,
            self::ROLE_GESTOR,
        ]);
    }

    public function canAccessManualAdministrador(): bool
    {
        return $this->hasAnyRole([
            self::ROLE_ADMINISTRADOR,
            self::ROLE_ALMACENISTA,
            self::ROLE_GESTOR,
        ]);
    }

    public function canAccessShipMarket(): bool
    {
        return $this->hasAnyRole([
            self::ROLE_ADMINISTRADOR,
            self::ROLE_SOLICITANTE,
        ]);
    }

    public function canAccessReportes(): bool
    {
        return $this->hasAnyRole([
            self::ROLE_ADMINISTRADOR,
            self::ROLE_ALMACENISTA,
            self::ROLE_GESTOR,
        ]);
    }

    public function canAccessGraficas(): bool
    {
        return $this->canAccessReportes();
    }

    /**
     * Get the name of the unique identifier for the user.
     * Used by Laravel's authentication system.
     */
    public function username(): string
    {
        return 'cedula';
    }

    public function responsableEmbarcacion()
    {
        return $this->hasMany(Embarcacion::class, 'responsable', 'cedula');
    }
}
