<?php

namespace Mtc\Core\Admin;

use Mtc\Core\AdminUser;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use \Illuminate\Database\Eloquent\Model as EloquentModel;
use Mtc\Modules\DoctorPolicy\Classes\DoctorPolicy;
use App\Casts\OptionalEncrypted;
use App\Src\Encryption;

/**
 * Admin\User class.  This looks after the admin_users table
 * using Eloquent base modelling
 *
 * This is the canonical model for the admin_users table. Mtc\Core\AdminUser is a
 * backwards-compatibility subclass of this model (see AL2U-T136).
 *
 * @author william.cameron
 */
class User extends EloquentModel
{
    use HasFactory;

    public $timestamps = false;
    protected $table = "admin_users";
    protected $fillable = ['username', 'password'];

    protected $casts = [
        'email' => OptionalEncrypted::class,
        'name' => OptionalEncrypted::class,
        'username' => OptionalEncrypted::class,
        'credentials' => OptionalEncrypted::class,
        'totp_secret' => OptionalEncrypted::class,
    ];

    /**
     * Attributes that are mirrored into deterministic *_hash columns so they can
     * be looked up while the source columns are encrypted at rest.
     *
     * @var string[]
     */
    public static $searchable = [
        'username',
        'email',
    ];

    /**
     * Keep the *_hash lookup columns in sync whenever an admin is saved.
     *
     * Uses static::saved() (not self::) so that each concrete class in the
     * hierarchy - both this model and the Mtc\Core\AdminUser subclass - registers
     * the listener under its own class name. Without this, saves made through the
     * subclass would not refresh their hashes.
     */
    protected static function boot(): void
    {
        parent::boot();

        static::saved(function (self $model) {
            Encryption::updateHashes($model);
        });
    }

    /**
     * Resolve the currently authenticated admin as an Eloquent model.
     */
    public static function getCurrent()
    {
        $legacy_admin_user = new \AdminUser();

        return $legacy_admin_user->eloquent();
    }

    /**
     * Look an admin up by username via the deterministic blind index.
     */
    public function scopeWhereUsername($query, string $username): Builder
    {
        return $query->where('username_hash', Encryption::makeHash($username));
    }

    /**
     * Save category restrictions for admin user
     *
     * @param int[] $categories list of categories admin user CAN edit
     */
    public function setRestrictedCategories($categories = [])
    {

        // Remove Roles that are no longer needed
        UserAllowedCategories::where('user_id', $this->id)
            ->delete();

        if (empty($categories)) {
            return;
        }
        // Loop through roles and make sure we have all of them set up
        foreach ($categories as $category) {
            UserAllowedCategories::firstOrCreate([
                'user_id' => $this->id,
                'category_id' => $category
            ]);
        }
    }

    /**
     * Relationship to the categories that are allowed for this admin
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function allowedCategories()
    {
        return $this->hasMany(UserAllowedCategories::class, 'user_id');
    }

    /**
     * Confirmed policies for a user
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function confirmedPolicies()
    {
        return $this->belongsToMany(DoctorPolicy::class, 'admin_user_policy_confirmations', 'user_id', 'policy_id')
            ->withPivot([
                'created_at'
            ]);
    }


    public function favouriteLinks()
    {
        return $this->belongsToMany(Menu::class, 'admin_users__favourite_links', 'user_id', 'menuitem_id');
    }


    public function linkedUsers()
    {
        return $this->hasMany(UserLinkedUser::class, 'user_id');
    }


    public function role()
    {
        return $this->belongsTo(Role::class, 'role');
    }

    public static function getAdminUser(?int $adminId=null)
    {
        $adminUser = new AdminUser();
        if (empty($adminUser->user)) {
            // check if its cli request
            if (php_sapi_name() === 'cli') {
                return self::query()
                    ->find(MTCADMIN_USERID);
            }

            static $cachedAdmin;

            if ($cachedAdmin instanceof self) {
                return $cachedAdmin;
            }

            $current = AdminUser::getCurrent();
            if (!$current) {
                return null;
            }

            $cachedAdmin = self::query()
                ->with('allowedCategories')
                ->find($current->id);

            return $cachedAdmin;
        }

        return $adminUser;
    }

    public static function getAllowedCategories()
    {
        $user = self::getAdminUser();
        if (!$user) {
            return collect();
        }

        if (!$user->relationLoaded('allowedCategories')) {
            $user->load('allowedCategories');
        }

        return $user->allowedCategories->pluck('category_id');
    }

    public static function newFactory()
    {
        return \Database\Factories\AdminUserFactory::new();
    }
}
