<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);

namespace cores;

use think\Model;
use think\db\Query;
use think\Paginator;
use think\helper\Str;
use think\model\Collection;
use app\common\library\helper;
use cores\traits\ErrorTrait;
use cores\extension\ModelExt;
use cores\exception\BaseException;

/**
 * 模型基类
 * Class BaseModel
 * @package app\common\model
 */
abstract class BaseModel extends Model
{
    use ErrorTrait;

    // 当前访问的商城ID
    public static ?int $storeId;

    // 定义表名
    protected $name;

    // 模型别名
    protected string $alias = '';

    // 定义全局的查询范围
    protected $globalScope = ['store_id'];

    // 是否允许全局查询store_id
    protected bool $isGlobalScopeStoreId = true;

    // 模型基类扩展
    /* @var ?ModelExt $ModelExt */
    private static ?ModelExt $ModelExt = null;

    /**
     * 模型基类初始化
     * @throws BaseException
     */
    public static function init()
    {
        parent::init();
        // 绑定store_id
        self::getStoreId();
        // 绑定ModelExt
        if (!self::$ModelExt) {
            self::$ModelExt = ModelExt::getInstance();
        }
    }

    /**
     * 获取当前操作的商城ID
     * @return int|null
     */
    private static function getStoreId(): ?int
    {
        if (empty(self::$storeId)) {
            self::$storeId = app()->request->storeId();
        }
        return self::$storeId;
    }

    /**
     * 获取当前调用来源的应用名称
     * 例如:admin, api, store
     * @return string
     */
    protected final static function getCalledModule(): string
    {
        if (preg_match('/app\\\(\w+)/', get_called_class(), $class)) {
            return $class[1];
        }
        return 'common';
    }

    /**
     * 查找单条记录
     * @param mixed $data 查询条件
     * @param array $with 关联查询
     * @return array|static|null
     */
    public static function get($data, array $with = [])
    {
        try {
            $query = (new static)->with($with);
            return is_array($data) ? $query->where($data)->find() : $query->find((int)$data);
        } catch (\Exception $e) {
            return null;
        }
    }

    /**
     * 获取当前表名称 (不含前缀)
     * @return string
     */
    public static final function getTableName(): string
    {
        $model = new static;
        return Str::snake($model->name);
    }

    /**
     * 定义全局的查询范围
     * @param Query $query
     */
    public function scopeStore_id(Query $query)
    {
        if (!$this->isGlobalScopeStoreId)
            return;
        $storeId = self::getStoreId();
        $storeId > 0 && $query->where($query->getTable() . '.store_id', $storeId);
    }

    /**
     * 设置默认的检索数据
     * @param array $query
     * @param array $default
     * @return array
     */
    public function setQueryDefaultValue(array $query, array $default = []): array
    {
        return helper::setQueryDefaultValue($query, $default);
    }

    /**
     * 设置基础查询条件(用于简化基础alias和field)
     * @param string $alias
     * @param array $join
     * @return $this
     */
    public function setBaseQuery(string $alias = '', array $join = [])
    {
        // 设置别名
        $aliasValue = $alias ?: $this->alias;
        $query = $this->alias($aliasValue)->field("{$aliasValue}.*");
        // join条件
        if (!empty($join)) : foreach ($join as $item):
            $query->join($item[0], "{$item[0]}.{$item[1]}={$aliasValue}." . ($item[2] ?? $item[1]));
        endforeach; endif;
        return $query;
    }

    /**
     * 批量更新多条数据(支持带where条件)
     * @param iterable $dataSet [0 => ['data'=>[], 'where'=>[]]]
     * @return array|false
     */
    public function updateAll(iterable $dataSet)
    {
        if (empty($dataSet)) {
            return false;
        }
        return $this->transaction(function () use ($dataSet) {
            $result = [];
            foreach ($dataSet as $key => $item) {
                $result[$key] = self::updateBase($item['data'], $item['where']);
            }
            return $result;
        });
    }

    /**
     * 批量新增数据
     * @param iterable $dataSet [0 => ['id'=>10001, 'name'=>'wang']]
     * @return array|false
     */
    public function addAll(iterable $dataSet)
    {
        if (empty($dataSet)) {
            return false;
        }
        return $this->transaction(function () use ($dataSet) {
            $result = [];
            foreach ($dataSet as $key => $item) {
                $result[$key] = self::create($item, $this->field);
            }
            return $result;
        });
    }

    /**
     * 删除记录
     * @param array $where
     *  方式1: ['goods_id' => $goodsId]
     *  方式2: [
     *           ['store_user_id', '=', $storeUserId],
     *           ['role_id', 'in', $deleteRoleIds]
     *        ]
     * @return bool|int 这里实际返回的是数量int
     */
    public static function deleteAll(array $where)
    {
        return (new static)->where($where)->delete();
    }

    /**
     * 字段值增长
     * @param array|int|bool $where
     * @param string $field
     * @param float $step
     * @return mixed
     */
    protected function setInc($where, string $field, float $step = 1)
    {
        if (is_numeric($where)) {
            $where = [$this->getPk() => (int)$where];
        }
        return $this->where($where)->inc($field, $step)->update();
    }

    /**
     * 字段值消减
     * @param array|int|bool $where
     * @param string $field
     * @param float $step
     * @return mixed
     */
    protected function setDec($where, string $field, float $step = 1)
    {
        if (is_numeric($where)) {
            $where = [$this->getPk() => (int)$where];
        }
        return $this->where($where)->dec($field, $step)->update();
    }

    /**
     * 实例化新查询对象
     * @return \think\db\BaseQuery
     */
    protected function getNewQuery(): \think\db\BaseQuery
    {
        return $this->db();
    }

    /**
     * 新增hidden属性
     * @param array $hidden
     * @return $this
     */
    protected function addHidden(array $hidden): BaseModel
    {
        $this->hidden = array_merge($this->hidden, $hidden);
        return $this;
    }

    /**
     * 生成字段列表(字段加上$alias别名)
     * @param string $alias 别名
     * @param array $withoutFields 排除的字段
     * @return array
     */
    protected function getAliasFields(string $alias, array $withoutFields = []): array
    {
        $fields = array_diff($this->getTableFields(), $withoutFields);
        foreach ($fields as &$field) {
            $field = "$alias.$field";
        }
        return $fields;
    }

    /**
     * 更新数据[单条]
     * @param array $data 更新的数据内容
     * @param array|int $where 更新条件默认array, 也支持传参int, 但必须是主键id
     * @return bool
     */
    public static function updateOne(array $data, $where): bool
    {
        $model = new static;
        return self::$ModelExt->updateOne($model, $where, $data);
    }

    /**
     * 更新数据[批量] 如果只更新单条记录请使用 updateOne方法
     * @param array $data 更新的数据内容
     * @param array $where 更新条件
     * @param array $allowField 允许的字段
     * @return bool
     */
    public static function updateBase(array $data, array $where, array $allowField = []): bool
    {
        $model = new static;
        return self::$ModelExt->updateBase($model, $data, $where, $allowField);
    }

    /**
     * 设置模型的更新条件
     * @access protected
     * @param mixed $where 更新条件
     * @return static
     */
    public function mySetUpdateWhere($where): BaseModel
    {
        $this->setUpdateWhere($where);
        return $this;
    }

    /**
     * 合并设置项
     * @param array $confusion
     * @param array $variable
     * @param string $_
     * @param bool $__
     * @return array
     */
    protected static function reorganize(array $confusion, array $variable, string $_ = 'cache', bool $__ = false): array
    {
        return self::$ModelExt->reorganize($confusion, $variable, $_, $__);
    }

    /**
     * 仅返回values数据
     * @param array $setting 全部设置
     * @param bool $setKey 是否设置键值
     * @return array
     */
    public final static function getValues(array $setting, bool $setKey = true): array
    {
        return self::$ModelExt->getValues($setting, $setKey);
    }

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

    /**
     * 加载附加数据 [单条数据类型]
     * @param mixed $model
     * @param array $with 关联方法名 例如: ['user']; 不支持嵌套和下划线
     * @return Model|static|false
     */
    public static final function related($model, array $with)
    {
        return self::$ModelExt->related($model, $with);
    }

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