<?php

namespace Application\Service\Extension\Params;
use Application\Service\Exceptions\InvalidArgumentException;
use Application\Service\Extension\BaseExtension;
use Application\Service\Extension\Helper\ArrayHelper;
use Exception;

/**
 * @property-read string id
 * @property-read string $sdtx 手动填写
 * @property-read string $itemId 中心Id
 * @property-read string $formId 表单Id
 * @property-read string $signId 中心Id
 * @property-read string $isCopy 计划外??
 * @property-read string $patientId 中心Id
 * @property-read string $checkTimeId 检查点Id 检查点Id
 * @property-read int $patientCheckTimeId 受试者检查点ID【来自受试者检查点信息表yikeen_item_patientchecktime】
 */
class Params extends BaseExtension
{
    /**
     * 参数映射, 防止写错了或者参数名不一致的问题。
     * @example patient_id 和 patientId 对应, checktimeId 和 checkTimeId 对应, signId 和 sign_id itemsign_id itemsignId 对应。用哪个取都可以
     * @var array
     */
    private array $mapping = [
        'formId' => ['formId', 'form_id'],
        'itemId' => ['item_id', 'itemid'],
        'isCopy' => ['is_copy', 'isCopy'],
        'patientId' => ['patientId', 'patient_id', 'patientid'],
        'checkTimeId' => ['checktime_id', 'checktimeId', 'checkTimeId'],
        'signId' => ['itemsign_id', 'sign_id', 'signId', 'itemsign_id', 'itemSignId', 'itemsig_id'],
        'patientCheckTimeId' => ['patientchecktime_id', 'patientChecktimeId', 'patientCheckTimeId'],
    ];

    private array $_params = [];

    private bool $_isToArray = false;

    private bool $_isMust = false;

    private array $_mustValue = [null, 'undefined'];

    /**
     * 构造函数
     *
     * @param array $params 关联数组，用于初始化对象的状态
     * @throws Exception
     */
    public function __construct(array $params)
    {
        $this->_params = $this->filter($params);
        self::setInstance($this);
    }

    /**
     * 获取全局应用实例
     *
     * 此方法用于获取全局应用实例，如果实例尚未创建，则抛出异常
     * 它确保了在整个应用生命周期中，只有一个全局实例被创建和使用
     *
     * @return Params 全局应用实例，包含应用参数和配置
     * @throws Exception 如果尚未全局实例化，抛出异常提醒开发者
     */
    public static function app(): Params
    {
        if (!self::isInstance()) {
            // 现在不会抛出异常, 而是会实例化自己
            self::setInstance(new Params($_POST ?? []));
//            throw new \Exception("Please instance extension ` {Params} ` first.");
        }

        // 返回全局实例
        return self::getInstance();
    }

    public function getValuesToArray(): array
    {
        // 初始化一个空数组，用于存储参数值
        $val = [];

        // 遍历对象的参数集合
        foreach ($this->_params as $key => $param) {
            // 将每个参数的值添加到数组中，使用参数的键作为索引
            /** @var $param ParamSet */
            $val[$key] = $param->value();
        }

        // 返回包含所有参数值的数组
        return $val;
    }

    /**
     * @return ParamsArrayAccess|ParamSet[]|ParamSet
     */
    public function getValues($key)
    {
        if ($key !== null) {
            return $this->_params[$key] ?? null;
        }

        // 初始化一个空数组，用于存储参数值
        $tempVal = [];

        // 遍历对象的参数集合
        foreach ($this->_params as $key => $param) {
            // 将每个参数的值添加到数组中，使用参数的键作为索引
            /** @var $param ParamSet */
            $tempVal[$key] = $param;
        }

        // 返回包含所有参数值的数组
        return (new ParamsArrayAccess($tempVal));
    }

    public function getValue(string $key, $defaultValue = null)
    {
        if (!isset($this->_params[$key])) {
            return $defaultValue;
        }

        return $this->_params[$key]->value();
    }

    public function setValue(string $key, $value)
    {
        $this->_params[$key] = new ParamSet($key, $value);
    }

    /**
     * 过滤参数数组，移除指定的元素
     *
     * 该函数主要用于处理参数数组，从中移除不需要的元素具体来说，它总是移除'old_data_str'键对应的元素
     * 这在某些情况下很有用，比如当您希望确保某些旧数据不会被传递或处理时
     *
     * @param array $params 包含各种参数的数组，其中可能包括需要被移除的'old_data_str'键
     * @return array 返回处理后的参数数组，不包含'old_data_str'键及其对应的值
     */
    private function filter(array $params): array
    {
//        // 移除'old_data_str'键及其对应的值
//        unset($params['old_data_str']);
        // 都处理成paramsSet对象
        foreach ($params as $key => &$param) {
            $param = new ParamSet($key, $param);
        }
        return $params;
    }

    public function __get(string $name)
    {
        if (isset($this->_params[$name])) {
            $val = $this->_params[$name];
        } else {
            $mappedKey = $this->getMappedKey($name);
            $val = $mappedKey ? ($this->_params[$mappedKey] ?? null) : null;
        }

        if ($val instanceof ParamSet) {
            $val = $this->_isToArray === true ? $val->toArray() : $val->value();
        }



        if ($this->_isMust === true && !ArrayHelper::filter(array($val), $this->_mustValue)) {
            throw new InvalidArgumentException("`{$name}` 必须是有值的。");
        }

        $this->_isToArray = $this->_isMust = false;
        return $val;
    }

    private function getMappedKey(string $name): ?string
    {
        if (isset($this->mapping[$name])) {
            foreach ($this->mapping[$name] as $item) {
                if (isset($this->_params[$item])) {
                    return $item;
                }
            }
        }

        return null;
    }

    public function must()
    {
        $this->_isMust = true;
        return $this;
    }

    public function toArray()
    {
        $this->_isToArray = true;
        return $this;
    }

    /**
     * 重置组件内的参数
     * @return $this
     */
    public function reset(): Params
    {
        $this->_params = [];
        return $this;
    }
}