<?php

namespace cores\extension;

use Exception;
use think\Model;
use think\Paginator;
use think\model\Collection;
use cores\BaseModel;
use cores\exception\BaseException;

/**
 * 模型基类的扩展层
 * Class ModelExt
 * @package cores\extension
 */
final class ModelExt
{
    // 创建静态私有的变量保存该类对象
    static private $instance;

    /**
     * 获取当前实例
     * @return ModelExt
     */
    public static function getInstance(): ModelExt
    {
        if (!self::$instance instanceof self) {
            self::$instance = new self;
        }
        return self::$instance;
    }

    /**
     * 更新数据[单条]
     * 扩展\think\Model::update方法, 目的可以返回更新的状态bool
     * @access public
     * @param BaseModel $model 模型类
     * @param array|int $where 更新条件
     * @param array $data 更新的数据内容
     * @return bool
     */
    public function updateOne($model, $where, array $data): bool
    {
        if (is_numeric($where)) {
            $where = [$model->getPk() => $where];
        }
        return $model->where($where)->limit(1)->save($data) !== false;
    }

    /**
     * 更新数据[批量]
     * 扩展\think\Model::update方法, 目的可以返回更新的状态bool
     * @access public
     * @param BaseModel $model 模型类
     * @param array $data 更新的数据内容
     * @param array $where 更新条件
     * @param array $allowField 允许的字段
     * @return bool
     */
    public static function updateBase($model, array $data, array $where, array $allowField = []): bool
    {
        if (!empty($allowField)) {
            $model->allowField($allowField);
        }
        return $model->mySetUpdateWhere($where)->exists(true)->save($data);
    }

    /**
     * 合并设置项
     * @param array $array1 系统默认配置
     * @param array $array2 用户自定义
     * @param string $_ 数据来源[后期加载附加数据]
     * @param false $__ 是否只显示value
     * @return array
     */
    public function reorganize(array $array1, array $array2, string $_ = 'cache', bool $__ = false): array
    {
        if (!in_array($_, ['cache', 'app'])) {
            return [];
        }
        $caching = [];
        foreach (($array1 + $array2) as $key => $val) {
            $caching[$key] = (isset($array1[$key]) && is_array($array1[$key]) && isset($array2[$key]) && is_array($array2[$key]))
                ? (is_assoc($array1[$key]) ? self::reorganize($array1[$key], $array2[$key], $_, $__) : $array2[$key])
                : ($array2[$key] ?? $array1[$key]);
        }
        return $__ === true ? self::getValues($caching, false) : $caching;
    }

    /**
     * 仅返回values数据
     * @param array $setting 全部设置
     * @param bool $setKey 是否设置键值
     * @return array
     */
    public static function getValues(array $setting, bool $setKey = true): array
    {
        $data = [];
        foreach ($setting as $k => $item) {
            $setKey ? $data[$item['key']] = $item['values'] : $data[$k] = $item;
        }
        return $data;
    }

    /**
     * 加载关联数据 [列表数据类型]
     * @param iterable $dataSet 数据集
     * @param array $with 关联方法名 例如: ['user']; 支持嵌套['user.avatar'] ['user' => 'avatar']
     * @param bool $isToArray 是否用数组格式输出
     * @return Collection|Paginator|iterable
     */
    public function preload(iterable $dataSet, array $with, bool $isToArray = false)
    {
        if (empty($dataSet)) {
            return $dataSet;
        }
        // 列表数据类型
        if ($dataSet instanceof Paginator || $dataSet instanceof Collection) {
            !$dataSet->isEmpty() && $dataSet->load($with);
            return $isToArray ? $dataSet->toArray() : $dataSet;
        }
        return $isToArray ? [] : $dataSet;
    }

    /**
     * 加载关联数据 [单条数据类型]
     * 单条记录时关联项不支持嵌套 例如: ['user.avatar']
     * @param $model
     * @param array $with
     * @return mixed|Model
     */
    public function related($model, array $with)
    {
        if ($model instanceof Model) {
            foreach ($with as $item) {
                $method = camelize($item);
                // mixd: call_user_func的方式不好用
                method_exists($model, $method) && $model->{$method};
            }
        }
        return $model;
    }

    /**
     * 获取隐藏的属性
     * @param $model
     * @param array $hidden 合并的隐藏属性
     * @return array
     */
    public function getHidden($model, array $hidden = []): array
    {
        return array_merge($model->hidden, $hidden);
    }

    // 防止使用new直接创建对象
    private function __construct()
    {
    }

    // 防止使用clone克隆对象
    private function __clone()
    {
    }
}