<?php

namespace Kylin\Activity;

use Closure;
use Illuminate\Auth\AuthManager;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Database\Eloquent\Model;
use Kylin\Activity\Exceptions\CouldNotLogActivity;

/**
 * 执行者解析器
 *
 * 负责解析活动日志的"执行者"（causer），即谁执行了操作。
 * 支持：
 *  - 自动从当前认证守卫获取用户
 *  - 按 ID 解析用户模型
 *  - 手动覆盖执行者（setCauser）
 *  - 自定义解析逻辑（resolveUsing）
 */
class CauserResolver
{
    /** 认证管理器 */
    protected AuthManager $authManager;

    /** 认证驱动名称 */
    protected string | null $authDriver;

    /** 自定义解析器闭包 */
    protected Closure | null $resolverOverride = null;

    /** 手动覆盖的执行者 */
    protected Model | null $causerOverride = null;

    /**
     * @param  Repository   $config      配置仓库
     * @param  AuthManager  $authManager 认证管理器
     */
    public function __construct(Repository $config, AuthManager $authManager)
    {
        $this->authManager = $authManager;

        $this->authDriver = $config['kylin-activity.default_auth_driver'] ?? null;
    }

    /**
     * 解析执行者
     *
     * 按优先级：手动覆盖 > 自定义解析器 > 默认认证用户
     *
     * @param  Model|int|string|null  $subject  执行者模型或 ID
     * @return Model|null
     */
    public function resolve(Model | int | string | null $subject = null): ?Model
    {
        if ($this->causerOverride !== null) {
            return $this->causerOverride;
        }

        if ($this->resolverOverride !== null) {
            $resultCauser = ($this->resolverOverride)($subject);

            if (! $this->isResolvable($resultCauser)) {
                throw CouldNotLogActivity::couldNotDetermineUser($resultCauser);
            }

            return $resultCauser;
        }

        return $this->getCauser($subject);
    }

    protected function resolveUsingId(int | string $subject): Model
    {
        $guard = $this->authManager->guard($this->authDriver);

        $provider = method_exists($guard, 'getProvider') ? $guard->getProvider() : null;
        $model = method_exists($provider, 'retrieveById') ? $provider->retrieveById($subject) : null;

        throw_unless($model instanceof Model, CouldNotLogActivity::couldNotDetermineUser($subject));

        return $model;
    }

    protected function getCauser(Model | int | string | null $subject = null): ?Model
    {
        if ($subject instanceof Model) {
            return $subject;
        }

        if (is_null($subject)) {
            return $this->getDefaultCauser();
        }

        return $this->resolveUsingId($subject);
    }

    /**
     * Override the resover using callback.
     */
    public function resolveUsing(Closure $callback): static
    {
        $this->resolverOverride = $callback;

        return $this;
    }

    /**
     * Override default causer.
     */
    public function setCauser(?Model $causer): static
    {
        $this->causerOverride = $causer;

        return $this;
    }

    protected function isResolvable(mixed $model): bool
    {
        return $model instanceof Model || is_null($model);
    }

    protected function getDefaultCauser(): ?Model
    {
        return $this->authManager->guard($this->authDriver)->user();
    }
}
