<?php

namespace App\Models;

use App\Notifications\PortalUserInvitationNotification;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Str;
use Laravel\Sanctum\HasApiTokens;

class PortalUser extends Authenticatable
{
    use HasFactory, Notifiable, HasApiTokens;

    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'portal_users';

    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        'company_id',
        'employee_id',
        'individual_registration_id',
        'name',
        'email',
        'password',
        'role',
        'invitation_token',
        'invitation_sent_at',
        'password_set_at',
        'email_verified_at',
        'two_factor_enabled',
        'two_factor_setup_at',
    ];

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

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

    /**
     * Generate a unique invitation token.
     */
    public function generateInvitationToken(): string
    {
        $this->invitation_token = Str::random(64);
        $this->invitation_sent_at = now();
        $this->save();

        return $this->invitation_token;
    }

    /**
     * Check if the invitation token has expired.
     */
    public function hasExpiredInvitationToken(): bool
    {
        if (! $this->invitation_sent_at) {
            return true;
        }

        return $this->invitation_sent_at->addDays(7)->isPast();
    }

    /**
     * Check if password has been set.
     */
    public function hasSetPassword(): bool
    {
        return ! is_null($this->password_set_at);
    }

    /**
     * Send the invitation notification.
     */
    public function sendInvitationNotification(): void
    {
        $this->notify(new PortalUserInvitationNotification($this->invitation_token));
    }

    /**
     * Get the company that the portal user belongs to.
     */
    public function company()
    {
        return $this->belongsTo(Company::class);
    }

    /**
     * Get the company employee record for this portal user.
     */
    public function companyEmployee()
    {
        return $this->belongsTo(CompanyEmployee::class, 'employee_id');
    }

    /**
     * Check if user is a company admin.
     */
    public function isCompanyAdmin(): bool
    {
        return $this->role === 'company_admin';
    }

    /**
     * Check if user is an employee.
     */
    public function isEmployee(): bool
    {
        return $this->role === 'employee';
    }

    /**
     * Check if user is an individual account holder.
     */
    public function isIndividual(): bool
    {
        return $this->role === 'individual';
    }

    /**
     * Get the individual registration linked to this portal user.
     */
    public function individualRegistration()
    {
        return $this->belongsTo(IndividualRegistration::class, 'individual_registration_id');
    }

    /**
     * Get the two-factor codes for this portal user.
     */
    public function twoFactorCodes()
    {
        return $this->morphMany(TwoFactorCode::class, 'authenticatable');
    }

    /**
     * Get the two-factor sessions for this portal user.
     */
    public function twoFactorSessions()
    {
        return $this->morphMany(TwoFactorSession::class, 'authenticatable');
    }

    /**
     * Check if two-factor authentication is enabled.
     */
    public function hasTwoFactorEnabled(): bool
    {
        return $this->two_factor_enabled;
    }

    /**
     * Check if user has completed two-factor setup.
     */
    public function hasCompletedTwoFactorSetup(): bool
    {
        return ! is_null($this->two_factor_setup_at);
    }

    /**
     * Check if user needs to set up two-factor authentication.
     */
    public function needsTwoFactorSetup(): bool
    {
        // Portal users need setup if 2FA is enabled but not yet set up
        return $this->two_factor_enabled && ! $this->hasCompletedTwoFactorSetup();
    }

    /**
     * Check if user can disable two-factor authentication.
     */
    public function canDisableTwoFactor(): bool
    {
        // Portal users CAN disable 2FA
        return true;
    }

    /**
     * Enable two-factor authentication for this user.
     */
    public function enableTwoFactor(): void
    {
        $this->two_factor_enabled = true;
        if (! $this->two_factor_setup_at) {
            $this->two_factor_setup_at = now();
        }
        $this->save();
    }

    /**
     * Disable two-factor authentication for this user.
     */
    public function disableTwoFactor(): void
    {
        $this->two_factor_enabled = false;
        $this->save();

        // Clean up any existing codes and sessions
        $this->twoFactorCodes()->delete();
        $this->twoFactorSessions()->delete();
    }
}
