<?php

namespace App\Models;

use App\Domains\Workspace\Models\Workspace;
use App\Domains\Workspace\Models\WorkspaceMember;
use Database\Factories\UserFactory;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
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\Str;
use Laravel\Sanctum\HasApiTokens;
use PragmaRX\Google2FA\Google2FA;

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

    protected $guarded = ['id'];

    protected static function booted(): void
    {
        static::creating(function (self $user) {
            if (empty($user->ulid)) {
                $user->ulid = Str::ulid();
            }
        });
    }

    /**
     * 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',
            'two_factor_confirmed_at' => 'datetime',
            'password' => 'hashed',
        ];
    }

    /** @return HasMany<Workspace, $this> */
    public function workspaces(): HasMany
    {
        return $this->hasMany(Workspace::class, 'owner_id');
    }

    /** @return BelongsTo<Workspace, $this> */
    public function defaultWorkspace(): BelongsTo
    {
        return $this->belongsTo(Workspace::class, 'default_workspace_id');
    }

    /** @return HasMany<WorkspaceMember, $this> */
    public function workspaceMemberships(): HasMany
    {
        return $this->hasMany(WorkspaceMember::class);
    }

    /** @return array<int, string> */
    public function enableTwoFactor(string $secret): array
    {
        $recoveryCodes = collect(range(1, 8))->map(fn () => implode('-', [
            strtoupper(substr(bin2hex(random_bytes(5)), 0, 5)),
            strtoupper(substr(bin2hex(random_bytes(5)), 0, 5)),
        ]))->all();

        $this->update([
            'two_factor_secret' => encrypt($secret),
            'two_factor_recovery' => encrypt(json_encode($recoveryCodes)),
        ]);

        return $recoveryCodes;
    }

    public function disableTwoFactor(): void
    {
        $this->update([
            'two_factor_secret' => null,
            'two_factor_recovery' => null,
            'two_factor_confirmed_at' => null,
        ]);
    }

    public function hasTwoFactorEnabled(): bool
    {
        return $this->two_factor_confirmed_at !== null;
    }

    public function verifyTwoFactor(string $code): bool
    {
        if (! $this->two_factor_secret) {
            return false;
        }

        $secret = decrypt($this->two_factor_secret);
        $google2fa = new Google2FA;

        return $google2fa->verifyKey($secret, $code) !== false;
    }
}
