<?php

namespace app\model;

use Exception;
use think\Collection;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\Model;
use think\Paginator;

class Base extends Model
{
    protected $autoWriteTimestamp = false;

    // 布尔值数字关系
    public const BOOL_FALSE = 0;
    public const BOOL_TRUE  = 1;

    public const COMMON_ON  = 1;
    public const COMMON_OFF = 0;

    //根据Id列表获取列表
    public static function getListByIds($ids)
    {
        if (count($ids) == 0 || empty($ids)) {
            return [];
        }
        return self::where('id', 'in', $ids)->select()->toArray();
    }

    //根据ID获取单条数据
    public static function getById($id)
    {
        if ($id <= 0) {
            return [];
        }
        return self::where('id', $id)->findOrEmpty()->toArray();
    }

    /**
     * 通过ID查找记录
     *
     * @param  int  $id
     * @param  array  $fields
     * @param  callable|null  $call
     * @return array|Model|null
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function findById(int $id, array $fields = [], callable $call = null)
    {
        $q = self::when(!empty($fields), function ($q) use ($fields) {
            $q->field($fields);
        });
        if ($call !== null) {
            $q = $call($q);
        }
        return $q->find($id);
    }

    /**
     * 查找单个记录
     *
     * @param  array  $where
     * @param  array  $fields
     * @param  callable|null  $call
     * @return array|Model|null
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function findOne(array $where = [], array $fields = [], callable $call = null)
    {
        $q = self::when(!empty($fields), function ($q) use ($fields) {
            $q->field($fields);
        })->where($where);

        if ($call !== null) {
            $q = $call($q);
        }
        return $q->find();
    }

    //根据ID更新数据
    public static function updateById($id, $data)
    {
        return self::where('id', $id)->update($data);
    }

    //根据where条件和排序获取记录
    public static function getListByWhereAndOrder($where, $order, $limit = 1)
    {
        return self::where($where)
            ->order($order)
            ->limit($limit)
            ->select()
            ->toArray();
    }

    /**
     * 根据ID删除数据
     *
     * @param  int  $id
     * @return bool
     */
    public static function deleteById(int $id): bool
    {
        return self::where('id', $id)->delete();
    }

    /**
     * 根据ID列表删除数据
     *
     * @param  array  $ids
     * @return bool
     */
    public static function deleteByIds(array $ids): bool
    {
        return self::whereIn('id', $ids)->delete();
    }

    /**
     * 排序
     *
     * @param  int  $id  调整ID
     * @param  string  $type  本次操作类型 向上、向下
     * @param  int  $num  移动位数
     * @param  string  $listType  列表的排序方式 默认为降序
     * @param  array  $where  额外条件 如限制在指定分类下 where[] = ['category_id', '=', 6]
     * @return array
     * @throws Exception
     */
    public static function sort(int $id, string $type, int $num = 1, string $listType = 'desc', array $where = []): array
    {
        $res  = ['code' => 0, 'msg' => 'success'];
        $item = self::getById($id);

        if (!$item) {
            $res['code'] = 1;
            $res['msg']  = '记录不存在';
            return $res;
        }

        if ($listType == 'desc') {
            if ($type == 'down') {
                $where[] = ['sort', '<', $item['sort']];
                $order   = "sort desc";
            } else {
                $where[] = ['sort', '>', $item['sort']];
                $order   = "sort asc";
            }
        } else {
            if ($type == 'up') {
                $where[] = ['sort', '<', $item['sort']];
                $order   = "sort desc";
            } else {
                $where[] = ['sort', '>', $item['sort']];
                $order   = "sort asc";
            }
        }

        $forSortItems = self::getListByWhereAndOrder($where, $order, $num);
        if (!empty($forSortItems)) {
            $updateData   = [];
            $forSortCount = count($forSortItems);
            for ($i = 0; $i < $forSortCount; $i++) {
                if ($i == 0) {
                    $updateData[] = [
                        'id'   => $forSortItems[$i]['id'],
                        'sort' => $item['sort']
                    ];
                } else {
                    $updateData[] = [
                        'id'   => $forSortItems[$i]['id'],
                        'sort' => $forSortItems[$i - 1]['sort']
                    ];
                }
            }
            $updateData[] = [
                'id'   => $item['id'],
                'sort' => $forSortItems[$i - 1]['sort']
            ];

            if (!empty($updateData)) {
                $obj = new static();
                $obj->saveAll($updateData);
                return $res;
            }
        }
        $res['code'] = 1;
        $res['msg']  = '无需调整';
        return $res;
    }

    /**
     * 查询列表 [带分页 适用于后台]
     *
     * @param  array  $data  查询数据 格式如下
     * [
     *   'fields' => ['id','title','desc'],//查询字段
     *   'where'  => [
     *                  ['name', 'like', '%thinkphp%'],
     *                  ['title', 'like', '%thinkphp%'],
     *                  ['id', '>', 0],
     *                  ['status', '=', 1],
     *               ],//查询条件
     *   'order'  => ['order'=>'desc','id'=>'desc'],//排序
     *   'size' => 50,//每页数量
     * ]
     * @param  array  $pageParams  分页参数 具体参考:https://www.kancloud.cn/manual/thinkphp6_0/1037638
     * @param  callable|null  $callback  复杂查询条件 使用闭包查询 此时建议data留空
     * @return Paginator
     * @throws DbException
     */
    public static function findListWithPaginate(array $data = [], array $pageParams = [], callable $callback = null): Paginator
    {
        $q      = new static();
        $fields = isset($data['fields']) && !empty($data['fields']) ? $data['fields'] : [];
        $where  = isset($data['where']) && !empty($data['where']) ? $data['where'] : [];
        $order  = isset($data['order']) && !empty($data['order']) ? $data['order'] : [];
        $limit  = $data['size'] ?? 20;

        if (count($where)) {
            $q = $q->where($where);
        }

        if (count($fields)) {
            $q = $q->field($fields);
        }

        if ($callback) {
            $q = $callback($q);
        }

        if (count($order)) {
            $q = $q->order($order);
        }

        $pageParams['list_rows'] = $limit;

        return $q->paginate($pageParams);
    }

    /**
     * 查询列表
     *
     * @param  array  $simpleWhere  简易查询条件
     * @param  array  $fields  查询字段 []表示全部
     * @param  int  $page  默认第一页 0不限制
     * @param  int  $limit  限制条数 0不限制
     * @param  callable|null  $callback  复杂的条件 使用闭包查询
     * @param  array  $orders 键值对,排序
     * @return array
     * @throws Exception
     */
    public static function findList(array $simpleWhere = [], array $fields = [], int $page = 1, int $limit = 0, callable $callback = null, array $orders = []): array
    {
        $q    = new static();
        $data = [
            'total'   => 0,
            'current' => $page,
            'size'    => $limit,
            'list'    => new Collection(),
        ];

        if (count($fields)) {
            $q = $q->field($fields);
        }

        if (count($simpleWhere)) {
            $q = $q->where($simpleWhere);
        }

        if ($callback) {
            $q = $callback($q);
        }

        $data['total'] = $q->count();

        if ($data['total']) {
            if (count($orders)) {
                $q = $q->order($orders);
            }
            if ($page) {
                $q = $q->page($page);
            }
            if ($limit == 0) {
                $q = $q->limit(1000);
            } else {
                $q = $q->limit($limit);
            }

            $data['list'] = $q->select();
        }

        return $data;
    }

    /**
     * 获取路径
     *
     * @param  int  $pid
     * @return string
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function getPath(int $pid): string
    {
        if ($pid == 0) {
            $path = ',0,';
        } else {
            $parent = self::findById($pid);
            if (empty($parent)) {
                $path = ',0,';
            } else {
                $path = $parent['path'].$parent['id'].',';
            }
        }

        return $path;
    }

    /**
     * 刷新路径
     *
     * @param  int  $pid
     * @param  array  $data  默认全部 若data不为空 至少包含[id,path, $pidField]
     * @param  string  $pidField  父级ID字段名 默认 pid
     * @throws Exception
     */
    public static function refreshPath(int $pid = 0, array $data = [], string $pidField = 'pid')
    {
        $data           = !empty($data) ? $data : self::column('id,path,'.$pidField);
        $updateAllPaths = [];
        self::recursionPath($pid, $data, $updateAllPaths);

        (new static())->saveAll($updateAllPaths);
    }

    /**
     * 获取递归最新路径
     *
     * @param  int  $pid
     * @param  array  $data  全部数据 尽量传全部数据
     * @param  array  $paths
     */
    public static function recursionPath(int $pid, array $data, array &$paths = [])
    {
        foreach ($data as $k => $v) {
            if ($pid == $v['pid']) {
                $arr       = [];
                $arr['id'] = $v['id'];
                if ($pid == 0) {
                    $arr['path'] = ',0,';
                } else {
                    $arr['path'] = $paths[$v['pid']]['path'].$v['pid'].',';
                }
                $paths[$v['id']] = $arr;

                unset($data[$k]);

                self::recursionPath($v['id'], $data, $paths);
            }
        }
    }

    /**
     * 获取所有后代ID[包含孙级]  仅对拥有path字段的表生效
     *
     * @param  int  $id
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function getAllChildrenIds(int $id): array
    {
        $item = self::find($id);
        if ($item && isset($item['path'])) {
            $path = $item['path'].$id.',';
            return self::where('path', 'like', $path.'%')->column('id');
        } else {
            return [];
        }
    }
}