<?php

declare(strict_types=1);

namespace App\Models;

use App\Services\OtpService;
use Filament\Panel;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
use Laravel\Sanctum\HasApiTokens;
use Filament\Models\Contracts\FilamentUser;
use Filament\Models\Contracts\HasName;
use Spatie\Permission\Traits\HasRoles;

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

    public const ROLE_ADMIN = 'admin';

    public const ROLE_WRITER = 'writer';

    public const ROLE_USER = 'user';

    public const ROLE_EMPLOYER = 'employer';

    public const ROLE_PRODUCER = 'producer';

    public const ROLE_EXPERT = 'expert';

    public const ACCOUNT_ADMIN = 'admin';

    public const ACCOUNT_EMPLOYER = 'employer';

    public const ACCOUNT_PRODUCER = 'producer';

    public const ACCOUNT_EXPERT = 'expert';

    public static function roleOptions(): array
    {
        return [
            self::ROLE_ADMIN => 'ادمین',
            self::ROLE_WRITER => 'نویسنده',
            self::ROLE_USER => 'کاربر',
            self::ROLE_EMPLOYER => 'کارفرما',
            self::ROLE_PRODUCER => 'تولیدکننده',
            self::ROLE_EXPERT => 'خدمات‌دهنده',
        ];
    }

    public static function accountTypeOptions(): array
    {
        return [
            self::ACCOUNT_ADMIN => 'ادمین',
            self::ACCOUNT_EMPLOYER => 'کارفرما',
            self::ACCOUNT_PRODUCER => 'تولیدکننده',
            self::ACCOUNT_EXPERT => 'خدمات‌دهنده',
        ];
    }

    protected $guarded = [];

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

    protected $fillable = [
        'first_name',
        'last_name',
        'username',
        'national_code',
        'national_cart_image',
        'cellphone',
        'dob',
        'phone',
        'email',
        'address',
        'province_id',
        'county_id',
        'postal_code',
        'latitude',
        'longitude',
        'otp',
        'password',
        'gender',
        'avatar',
        'desc',
        'email_verified_at',
        'login_type',
        'account_type',
        'avatar_verified_at',
        'last_online_at',
        'wallet',
        'status',
        'status_desc',
        'is_blocked',
        'blocked_at',
        'blocked_reason',
        'notification_settings',
    ];

    protected $appends = [
        'last_login',
        'last_ip',
        'name',
    ];

    protected $with = ['roles'];

    protected $withCount = [];

    protected $guard_name = 'api';

    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope('order', function (Builder $builder): void {
            $builder->orderByDesc('created_at');
        });

        static::creating(function ($item): void {
            if (empty($item->avatar)) {
                $item->avatar = 'files/default-avatar.png';
            }
        });

        static::updating(function ($item): void {
            if (empty($item->avatar)) {
                $item->avatar = 'files/default-avatar.png';
            }
        });
    }

    protected function name(): Attribute
    {
        return Attribute::make(get: function () {
            $full = trim(($this->first_name ?? '') . ' ' . ($this->last_name ?? ''));

            return $full !== '' ? $full : ('کاربر - ' . ($this->id ?? ''));
        });
    }

    protected function lastLogin(): Attribute
    {
        return Attribute::make(get: function () {
            $login = Log::where('user_id', $this->id)->where('event', 'auth')->latest()->first();

            return $login?->created_at;
        });
    }

    protected function lastIp(): Attribute
    {
        return Attribute::make(get: function () {
            $log = Log::where('user_id', $this->id)->latest()->first();

            return $log?->ip;
        });
    }

    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'dob' => 'date',
            'is_blocked' => 'boolean',
            'blocked_at' => 'datetime',
            'last_online_at' => 'datetime',
            'wallet' => 'integer',
            'status' => 'integer',
            'latitude' => 'decimal:8',
            'longitude' => 'decimal:8',
            'notification_settings' => 'array',
            'two_factor_enabled_for_login' => 'boolean',
            'two_factor_enabled_for_withdrawal' => 'boolean',
            'two_factor_enabled_for_project_activation' => 'boolean',
        ];
    }

    /**
     * @return array{email_notifications: bool, order_confirm: bool, order_cancel: bool}
     */
    public function notificationSettings(): array
    {
        $defaults = [
            'email_notifications' => true,
            'order_confirm' => false,
            'order_cancel' => false,
        ];

        $stored = is_array($this->notification_settings) ? $this->notification_settings : [];

        return [
            'email_notifications' => (bool) ($stored['email_notifications'] ?? $defaults['email_notifications']),
            'order_confirm' => (bool) ($stored['order_confirm'] ?? $defaults['order_confirm']),
            'order_cancel' => (bool) ($stored['order_cancel'] ?? $defaults['order_cancel']),
        ];
    }

    public function walletPayments()
    {
        return $this->hasMany(Payment::class, 'user_id')
            ->where('gateway', 'wallet');
    }

    public function walletTransactions(): \Illuminate\Database\Eloquent\Relations\HasMany
    {
        return $this->hasMany(WalletTransaction::class);
    }

    public function bankCards(): \Illuminate\Database\Eloquent\Relations\HasMany
    {
        return $this->hasMany(BankCard::class);
    }

    public function withdrawalRequests(): \Illuminate\Database\Eloquent\Relations\HasMany
    {
        return $this->hasMany(WithdrawalRequest::class);
    }

    public function invoices(): \Illuminate\Database\Eloquent\Relations\HasMany
    {
        return $this->hasMany(Invoice::class);
    }

    public function producer(): \Illuminate\Database\Eloquent\Relations\HasOne
    {
        return $this->hasOne(Producer::class);
    }

    public function expert(): \Illuminate\Database\Eloquent\Relations\HasOne
    {
        return $this->hasOne(Expert::class);
    }

    public function province(): \Illuminate\Database\Eloquent\Relations\BelongsTo
    {
        return $this->belongsTo(\Mortezaa97\Regions\Models\Province::class, 'province_id');
    }

    public function county(): \Illuminate\Database\Eloquent\Relations\BelongsTo
    {
        return $this->belongsTo(\Mortezaa97\Regions\Models\County::class, 'county_id');
    }

    public function resolveAccountType(): string
    {
        if ($this->account_type) {
            return (string) $this->account_type;
        }

        if ($this->hasRole(self::ROLE_ADMIN)) {
            return self::ACCOUNT_ADMIN;
        }
        if ($this->hasRole(self::ROLE_PRODUCER) || ($this->relationLoaded('producer') && $this->producer)) {
            return self::ACCOUNT_PRODUCER;
        }
        if ($this->hasRole(self::ROLE_EXPERT) || ($this->relationLoaded('expert') && $this->expert)) {
            return self::ACCOUNT_EXPERT;
        }
        if ($this->hasRole(self::ROLE_EMPLOYER)) {
            return self::ACCOUNT_EMPLOYER;
        }

        return self::ACCOUNT_EMPLOYER;
    }

    public function primaryRole(): ?string
    {
        $priority = [
            self::ROLE_ADMIN,
            self::ROLE_PRODUCER,
            self::ROLE_EXPERT,
            self::ROLE_EMPLOYER,
            self::ROLE_WRITER,
            self::ROLE_USER,
        ];

        foreach ($priority as $role) {
            if ($this->hasRole($role)) {
                return $role;
            }
        }

        if ($this->relationLoaded('roles')) {
            return $this->roles->first()?->name;
        }

        return $this->getRoleNames()->first();
    }

    public function isBlocked(): bool
    {
        return (bool) $this->is_blocked || (int) $this->status === 0;
    }

    public function isActiveAccount(): bool
    {
        return ! $this->isBlocked();
    }

    public function save(array $options = [])
    {
        return parent::save($options);
    }

    public function getAuthPassword()
    {
        $otpService = new OtpService;

        if ($this->hasAnyRole(['admin', 'user', 'employer', 'producer', 'expert']) || $this->login_type == 1) {
            return $this->password;
        }
        $latestCode = $otpService->getLatestCode($this->cellphone);

        return $latestCode->code;
    }

    public function findForPassport($username)
    {
        if (filter_var($username, FILTER_VALIDATE_EMAIL)) {
            return $this->where('email', $username)->first();
        }

        return $this->where('cellphone', $username)->first();
    }

    public function validateForPassportPasswordGrant($password)
    {
        if ($this->login_type == 3) {
            return true;
        }
        $authPassword = $this->getAuthPassword();

        return Hash::check($password, $authPassword);
    }

    public function canAccessPanel(Panel $panel): bool
    {
        return $this->hasAnyRole(['admin', 'writer']) && $this->isActiveAccount();
    }

    public function getFilamentName(): string
    {
        if ($this->first_name || $this->last_name) {
            return trim("{$this->first_name} {$this->last_name}");
        }

        return (string) ($this->name ?? 'کاربر');
    }
}
