<?php

namespace Api\PhpUtils\Mongo;

use MongoDB\Client;

abstract class Base
{
    protected $collection = null;

    /**
     * 根据配置，构建collection连接实例
     * https://docs.mongodb.com/php-library/v1.4/
     */
    public function __construct()
    {
        $db_conf = config('mongo', $this->getConfigIndex());
        if (!empty($db_conf) && isset($db_conf['uriOptions']) && isset($db_conf['driverOptions'])) {
            //某些配置项值必须是数字
            foreach ($db_conf['uriOptions'] as $key => $v) {
                $keys = ['connectTimeoutMS', 'socketTimeoutMS', 'wTimeoutMS'];
                if (in_array($key, $keys)) {
                    $db_conf['uriOptions'][$key] = intval($v);
                }
            }
            //初始化客户端
            //兼容多个复制集的情况（rs_interact1.mongo.int.yidian-inc.com:27017,rs_interact2.mongo.int.yidian-inc.com:27017,rs_interact3.mongo.int.yidian-inc.com:27017）
            if(strpos($db_conf['host'], ':') !== false){
                $client = new Client(
                    "mongodb://{$db_conf['host']}",
                    $db_conf['uriOptions'],
                    $db_conf['driverOptions']
                );
            }else{
                $client = new Client(
                    "mongodb://{$db_conf['host']}:{$db_conf['port']}",
                    $db_conf['uriOptions'],
                    $db_conf['driverOptions']
                );
            }
            //初始化数据库
            $this->collection = $client->selectCollection($db_conf['db'], $this->getCollectionName());
        }
    }

    /**
     * 获取子类实例
     * @return object|string
     */
    public static function getInstance()
    {
        try {
            $dao_name = get_called_class();
            if (empty(static::$instance)) {
                static::$instance = new $dao_name();
            }
            return static::$instance;
        } catch (\Exception $e) {
            return 'mongo connect failed';
        }
    }

    /**
     * 获取数据库配置，由子类实现
     * @return string
     */
    abstract protected function getConfigIndex();

    /**
     * 获取表名，由子类实现
     * @return string
     */
    abstract protected function getCollectionName();

    /**
     * 获取where条件数组，由子类实现
     * @param array $params
     * @return array
     */
    abstract protected function getWhere($params);

    /**
     * 获取query options数组，由子类实现
     * @param array $fields
     * @param array $sort
     * @param int $limit
     * @return mixed
     */
    abstract protected function getQueryOptions($fields = [], $sort = [], $limit = null);

    /**
     * 新增
     * @param array $data
     * @return int(新增行数)|string(错误信息)
     */
    public function add($data)
    {
        try {
            if (!is_array($data) || empty($data)) {
                throw new \Exception('data error');
            }
            $ret = $this->collection->insertOne($data);
            $cnt = $ret->getInsertedCount();
            if (empty($cnt)) {
                throw new \Exception('add failed');
            }
            return $cnt;
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }

    /**
     * 新增多条
     * @param array $data
     * @return int(新增行数)|string(错误信息)
     */
    public function addMany($data)
    {
        try {
            if (!is_array($data) || empty($data) || count($data) == count($data, COUNT_RECURSIVE)) {
                throw new \Exception('data error');
            }
            $ret = $this->collection->insertMany($data);
            $cnt = $ret->getInsertedCount();
            if (empty($cnt)) {
                throw new \Exception('addMany failed');
            }
            return $cnt;
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }
    /**
     * 删除
     * @param array $params
     * @return int(删除行数)|string(错误信息)
     */
    public function delete($params)
    {
        try {
            if (!is_array($params) || empty($params)) {
                throw new \Exception('params error');
            }
            $where = $this->getWhere($params);
            if (empty($where)) {
                throw new \Exception('where can not be null');
            }
            $ret = $this->collection->deleteMany($where);
            $cnt = $ret->getDeletedCount();
            if (empty($cnt)) {
                throw new \Exception('delete failed');
            }
            return $cnt;
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }
    /**
     * 更新
     * @param array $params
     * @param array $data
     * @return int(更新行数)|string(错误信息)
     */
    public function update($params, $data)
    {
        try {
            if (empty($params) || !is_array($params) || empty($data) || !is_array($data)) {
                throw new \Exception('params or data error');
            }
            $where = $this->getWhere($params);
            if (empty($where)) {
                throw new \Exception('where can not be null');
            }
            $data = [
                '$set' => $data
            ];
            $ret = $this->collection->updateMany($where, $data);
            $cnt = $ret->getModifiedCount();
            if (empty($cnt)) {
                throw new \Exception('update failed');
            }
            return $cnt;
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }
    /**
     * 获取单条结果
     * @param array $params
     * @param array $fields
     * @param array $sort
     * @param array $limit ['start' => 0, 'count' => 10]
     * @return string|array
     */
    public function get($params, $fields = [], $sort = [], $limit = [])
    {
        try {
            if (!is_array($params) || empty($params)) {
                throw new \Exception('params error');
            }
            $ret = $this->collection->findOne($this->getWhere($params), $this->getQueryOptions($fields, $sort, $limit));
            return empty($ret) ? [] : $ret;
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }
    /**
     * 获取多条结果
     * @param array $params
     * @param array $fields
     * @param array $sort
     * @param array $limit
     * @return string|array
     */
    public function getMany($params, $fields = [], $sort = [], $limit = [])
    {
        try {
            if (!is_array($params) || empty($params)) {
                throw new \Exception('params error');
            }
            $ret = [];
            $cursor = $this->collection->find($this->getWhere($params), $this->getQueryOptions($fields, $sort, $limit));
            foreach ($cursor as $k => $document) {
                $ret[$k] = $document;
            }
            return empty($ret) ? [] : $ret;
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }

    /**
     * count
     * @param array $params
     * @param string $column
     * @return string|int
     */
    public function count($params)
    {
        try {
            if (!is_array($params) || empty($params)) {
                throw new \Exception('params error');
            }
            return $cursor = $this->collection->countDocuments($this->getWhere($params));
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }
}
