<?php

namespace Api\PhpUtils\Mysql;

use Medoo\Medoo;

abstract class Base
{
    private $db = null;

    /**
     * 根据数据库配置参数，构建数据库连接实例
     * 具体PDO配置参考 https://www.cnblogs.com/hf8051/p/4673030.html
     * @param string $db_type 写库|读库
     * @throws $e 抛出异常，由getWriteInstance()或getInstance()捕获
     */
    public function __construct($db_type)
    {
        try {
            $db_conf = config('mysql', $this->getConfigIndex());
            if (!empty($db_conf) && isset($db_conf[$db_type]) && !empty($db_conf[$db_type])) {
                //设置连接超时时间
                $connect_timeout = 30;
                if(isset($db_conf['connect_timeout'])){
                    $connect_timeout = intval($db_conf['connect_timeout']);
                }
                //设置读超时时间
                $read_timeout = 30;
                if(isset($db_conf['read_timeout'])){
                    $read_timeout = intval($db_conf['read_timeout']);
                }
                ini_set('mysqlnd.net_read_timeout', $read_timeout);
                $this->db = new Medoo(
                    [
                        'database_type' => $db_conf['database_type'],
                        'port' => $db_conf['port'],
                        'database_name' => $db_conf['name'],
                        'charset' => $db_conf['charset'],
                        'server' => $db_conf[$db_type]['host'],
                        'username' => $db_conf[$db_type]['username'],
                        'password' => $db_conf[$db_type]['password'],
                        'option' => [
                            \PDO::ATTR_STRINGIFY_FETCHES => false,
                            \PDO::ATTR_EMULATE_PREPARES => false,
                            \PDO::ATTR_CASE => \PDO::CASE_NATURAL,
                            \PDO::ATTR_TIMEOUT => $connect_timeout
                        ]
                    ]
                );
            }else{
                throw new \Exception('mysql config not exist');
            }
        }catch (\Exception $e){
            throw $e;
        }
    }

    /**
     * 获取子类写库实例
     * @return object|string （错误信息）
     */
    public static function getWriteInstance()
    {
        try {
            $dao_name = get_called_class();
            if (empty(static::$write)) {
                static::$write = new $dao_name('write');
            }
            return static::$write;
        } catch (\Exception $e) {
            return 'mysql getWriteInstance failed : ' . $e->getMessage();
        }
    }

    /**
     * 获取子类读库实例（默认）
     * @return object|string （错误信息）
     */
    public static function getInstance()
    {
        try {
            $dao_name = get_called_class();
            if (empty(static::$read)) {
                static::$read = new $dao_name('read');
            }
            return static::$read;
        } catch (\Exception $e) {
            return 'mysql getInstance failed : ' . $e->getMessage();
        }
    }

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

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

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

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

    /**
     * 构造排序
     * @param array $order ['id' => 'desc', 'name' => 'asc']
     * @return array
     */
    private function getOrder($order)
    {
        if (is_array($order) && !empty($order)) {
            foreach ($order as $k => $v) {
                $order[$k] = strtoupper($v);
            }
        }
        return $order;
    }
    /**
     * 构造分页
     * @param array $limit ['start' => 0, 'count' => 10]
     * @return array
     */
    private function getLimit($limit)
    {
        $start = isset($limit['start']) ? intval($limit['start']) : 0;
        $count = isset($limit['count']) ? intval($limit['count']) : 100;
        return [$start, $count];
    }
    /**
     * 新增
     * @param array $data
     * @return int(最新的id)|string(错误信息)
     */
    public function add($data)
    {
        try {
            if (!is_array($data) || empty($data)) {
                throw new \Exception('data error');
            }
            $this->db->insert($this->getTableName(), $data);
            $new_id = $this->db->id();
            if (empty($new_id)) {
                throw new \Exception('add failed');
            }
            return $new_id;
        } 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->db->insert($this->getTableName(), $data);
            $cnt = $ret->rowCount();
            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->db->delete($this->getTableName(), $where);
            $cnt = $ret->rowCount();
            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');
            }
            $ret = $this->db->update($this->getTableName(), $data, $where);
            $cnt = $ret->rowCount();
            if (empty($cnt)) {
                throw new \Exception('update failed');
            }
            return $cnt;
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }
    /**
     * 获取单条结果
     * @param array $params
     * @param array|string $column
     * @param array $order
     * @return string|array
     */
    public function get($params, $column = '*', $order = [])
    {
        try {
            if (!is_array($params) || empty($params)) {
                throw new \Exception('params error');
            }
            $where = $this->getWhere($params);
            if (is_array($order) && !empty($order)) {
                $where['ORDER'] = $this->getOrder($order);
            }
            $ret = $this->db->get($this->getTableName(), $column, $where);
            return empty($ret) ? [] : $ret;
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }
    /**
     * 获取多条结果
     * @param $params
     * @param array|string $column
     * @param array $order ['id' => 'desc', 'name' => 'asc']
     * @param array $limit ['start' => 0, 'count' => 10]
     * @return string|array
     */
    public function getMany($params, $column = '*', $order = [], $limit = [])
    {
        try {
            if (!is_array($params) || empty($params)) {
                throw new \Exception('params error');
            }
            $where = $this->getWhere($params);
            if (is_array($order) && !empty($order)) {
                $where['ORDER'] = $this->getOrder($order);
            }
            if (is_array($limit) && !empty($limit)) {
                $where['LIMIT'] = $this->getLimit($limit);
            }
            $ret = $this->db->select($this->getTableName(), $column, $where);
            return empty($ret) ? [] : $ret;
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }

    /**
     * count
     * @param array $params
     * @param string $column
     * @return string|int
     */
    public function count($params, $column = '*')
    {
        try {
            if (!is_array($params) || empty($params) || !is_string($column) || empty($column)) {
                throw new \Exception('params error');
            }
            return $this->db->count($this->getTableName(), $column, $this->getWhere($params));
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }
    /**
     * max
     * @param array $params
     * @param string $column
     * @return string|int
     */
    public function max($params, $column)
    {
        try {
            if (!is_array($params) || empty($params) || !is_string($column) || empty($column)) {
                throw new \Exception('params error');
            }
            return $this->db->max($this->getTableName(), $column, $this->getWhere($params));
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }
    /**
     * min
     * @param array $params
     * @param string $column
     * @return string|int
     */
    public function min($params, $column)
    {
        try {
            if (!is_array($params) || empty($params) || !is_string($column) || empty($column)) {
                throw new \Exception('params error');
            }
            return $this->db->min($this->getTableName(), $column, $this->getWhere($params));
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }
    /**
     * avg
     * @param array $params
     * @param string $column
     * @return string|int
     */
    public function avg($params, $column)
    {
        try {
            if (!is_array($params) || empty($params) || !is_string($column) || empty($column)) {
                throw new \Exception('params error');
            }
            return $this->db->avg($this->getTableName(), $column, $this->getWhere($params));
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }
    /**
     * sum
     * @param array $params
     * @param string $column
     * @return string|int
     */
    public function sum($params, $column)
    {
        try {
            if (!is_array($params) || empty($params) || !is_string($column) || empty($column)) {
                throw new \Exception('params error');
            }
            return $this->db->sum($this->getTableName(), $column, $this->getWhere($params));
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }
}
