<?php namespace sc;

class DB
{
    /** @var  \PDO PDO连接对象 */
    private $pdo;
    /** @var  string 表名 */
    private $table;
    /** @var string 表前缀 */
    private $prefix;
    /** @var  string 执行状态 */
    private $status;
    /** @var  array 语句数组 */
    private $statement;
    /** @var  array 绑定数据 */
    private $data;

    /**
     * DB constructor.
     * 创建数据库连接
     * 添加表名前缀
     *
     * @param array $conf
     */
    public function __construct($conf)
    {
        $dsn = implode('', [
            'mysql',
            ':host=', $conf['host'],
            ';port=', $conf['port'],
            ';dbname=', $conf['dbname'],
            ';charset=', $conf['charset']
        ]);

        $this->pdo = new \PDO($dsn, $conf['username'], $conf['password']);

        $this->prefix = $conf['prefix'];
    }

    /**
     * 选择表
     *
     * @param string $table
     *
     * @return $this
     */
    public function table($table)
    {
        if (isset($this->status)) {
            die('在执行其他操作前请选择表');
        }

        $this->table = $this->prefix . $table;
        $this->statement = $this->data = [];
        $this->status = 'table';

        return $this;
    }

    /**
     * 查询操作
     *
     * @param string $columns
     *
     * @return $this
     */
    public function find($columns)
    {
        if ('table' != $this->status) {
            die('请勿执行增删改查混合操作');
        }

        $this->statement[0] = 'SELECT ' . $columns . ' FROM ' . $this->table;
        $this->status = 'select';

        return $this;
    }

    /**
     * WHERE子句
     *
     * @param string $conditions
     * @param array  $data
     *
     * @return $this
     */
    public function where($conditions, $data = null)
    {
        if ('select' != $this->status) {
            die('仅查询语句接受WHERE子句');
        }

        $this->statement[1] = 'WHERE ' . $conditions;

        if (null !== $data) {
            $this->data = $data;
        }

        return $this;
    }

    /**
     * ORDER子句
     *
     * @param string $order
     *
     * @return $this
     */
    public function order($order)
    {
        if ('select' != $this->status) {
            die('仅查询语句接受ORDER子句');
        }

        $this->statement[4] = 'ORDER BY ' . $order;

        return $this;
    }

    /**
     * LIMIT子句
     *
     * @param int $offset
     * @param int $limit
     *
     * @return $this
     */
    public function limit($offset, $limit)
    {
        if ('select' != $this->status) {
            die('仅查询语句接受LIMIT子句');
        }

        $this->statement[5] = 'LIMIT ' . $offset . ', ' . $limit;

        return $this;
    }

    /**
     * 取出一条数据
     *
     * @return array|string
     */
    public function one()
    {
        if ('select' != $this->status) {
            die('仅查询语句可取出返回结果');
        }

        if (!isset($this->statement[5])) {
            $this->statement[5] = 'LIMIT 1';
        }

        $stmt = $this->pdo->prepare(implode(' ', $this->statement));

        if (!isset($this->data)) {
            $this->data = null;
        }

        $stmt->execute($this->data);
        unset($this->status);

        if ('00000' === $stmt->errorCode()) {
            return $stmt->fetch(\PDO::FETCH_ASSOC);
        } else {
            return $stmt->errorInfo()[2];
        }
    }

    /**
     * 取出多条数据
     *
     * @return array|string
     */
    public function all()
    {
        if ('select' != $this->status) {
            die('仅查询语句可取出返回结果');
        }

        $stmt = $this->pdo->prepare(implode(' ', $this->statement));

        if (!isset($this->data)) {
            $this->data = null;
        }

        $stmt->execute($this->data);
        unset($this->status);

        if ('00000' === $stmt->errorCode()) {
            return $stmt->fetchAll(\PDO::FETCH_ASSOC);
        } else {
            return $stmt->errorInfo()[2];
        }
    }

    /**
     * 插入操作
     *
     * @param array $data
     *
     * @return $this
     */
    public function insert($data)
    {
        if ('table' != $this->status) {
            die('请勿执行增删改查混合操作');
        }

        $columns = $values = '';
        foreach ($data as $column => $datum) {
            $this->data[] = $datum;
            $columns .= $column . ', ';
            $values .= '?, ';
        }

        $this->statement[0] = 'INSERT INTO ' . $this->table . '(' . substr($columns, 0, -2) . ') VALUE(' . substr($values, 0, -2) . ')';
        $this->status = 'insert';

        return $this;
    }

    /**
     * 删除操作
     *
     * @param string $conditions
     * @param array  $data
     *
     * @return $this
     */
    public function delete($conditions, $data = null)
    {
        if ('table' != $this->status) {
            die('请勿执行增删改查混合操作');
        }

        $this->statement[0] = 'DELETE FROM ' . $this->table . ' WHERE ' . $conditions;

        if (null !== $data) {
            $this->data = $data;
        }
        $this->status = 'delete';

        return $this;
    }

    /**
     * 修改操作
     *
     * @param array  $data
     * @param string $conditions
     * @param array  $value
     *
     * @return $this
     */
    public function update($data, $conditions, $value = null)
    {
        if ('table' != $this->status) {
            die('请勿执行增删改查混合操作');
        }

        $values = '';
        foreach ($data as $column => $datum) {
            $values .= $column . ' = ?, ';
            $this->data[] = $datum;
        }

        $this->statement[0] = 'UPDATE ' . $this->table . ' SET ' . substr($values, 0, -2) . ' WHERE ' . $conditions;

        if (null != $value) {
            $this->data = array_merge($this->data, $value);
        }
        $this->status = 'update';

        return $this;
    }

    /**
     * 取出操作影响行数
     *
     * @return int|string
     */
    public function row()
    {
        if (!in_array($this->status, ['insert', 'delete', 'update'])) {
            die('仅在操作完成后才能取出受操作影响行数');
        }

        $stmt = $this->pdo->prepare(implode('  ', $this->statement));

        if (!isset($this->data)) {
            $this->data = null;
        }

        $stmt->execute($this->data);
        unset($this->status);

        if ('00000' === $stmt->errorCode()) {
            return $stmt->rowCount();
        } else if ('23000' === $stmt->errorCode()) {
            // 唯一索引冲突
            return 23000;
        } else {
            return $stmt->errorInfo()[2];
        }
    }

    /**
     * 统计字段
     *
     * @param string $conditions
     * @param array  $data
     * @param string $column
     *
     * @return int
     */
    public function count($conditions = null, $data = null, $column = 'id')
    {
        if ('table' != $this->status) {
            die('请勿执行增删改查混合操作');
        }

        $this->statement[] = 'SELECT COUNT(' . $column . ') FROM ' . $this->table;

        if (null !== $conditions) {
            $this->statement[] .= 'WHERE ' . $conditions;
        }

        $stmt = $this->pdo->prepare(implode(' ', $this->statement));

        $stmt->execute($data);
        unset($this->status);

        if ('00000' === $stmt->errorCode()) {
            return (int)$stmt->fetch(\PDO::FETCH_NUM)[0];
        } else {
            return $stmt->errorInfo()[2];
        }
    }

    /**
     * SQL打印方法
     *
     * @return string
     */
    public function sql()
    {
        $sql = implode(' ', $this->statement);
        if (null != $this->data) {
            foreach ($this->data as $datum) {
                if (!is_numeric($datum)) {
                    $datum = "'$datum'";
                }
                $sql = preg_replace('/\?/', $datum, $sql, 1);
            }
        }

        return $sql;
    }
}