<?php

namespace fastphp\db;

use \PDOStatement;

class Sql
{
    //数据库表名
    protected $table;

    //where 和 order拼装后的条件
    private $filter = '';

    //pdo 绑定的参数集合
    private $param = array();

    private $field = '*';

    /**
     * 查询条件拼接
     *
     * @param  $where 条件
     * @return $this
     * ['id'=>1]   或者  '`id`=1' 尽量不要使用string  因为没有做防sql注入
     * 多条件查询
     * ['id'=>['<',100]]
     */
    public function where($where)
    {
        $this->filter .= '';
        if ($where) {
            if (!strpos($this->filter, 'WHERE') !== false) {
                $this->filter .= ' WHERE ';
            } else {
                $this->filter .= ' AND ';
            }
            if (is_string($where)) {
                $this->filter .= $where;
            } else {
                $flag = true;
                $ins = ['in', 'IN', 'not in', 'NOT IN'];
                foreach ($where as $key => $value) {
                    if ($flag) {
                        $flag = false;
                        if (is_array($value)) {
                            if (in_array($value[0], $ins)) {
                                $this->filter .= ' `' . $key . '` ' . $value[0] . '(\'' . implode('\',\'', $value[1]) . '\')';
                            } else {
                                $param_key = $this->paramIsExit($key, $this->param);
                                $this->filter .= ' `' . $key . '` ' . $value[0] . ' :' . $param_key;
                                $this->param[$param_key] = $value[1];
                            }
                        } else {
                            $this->filter .= ' `' . $key . '` = :' . $key;
                            $this->param[$key] = $value;
                        }
                    } else {
                        if (is_array($value)) {
                            if (in_array($value[0], $ins)) {
                                $this->filter .= ' AND `' . $key . '` ' . $value[0] . '(\'' . implode('\',\'', $value[1]) . '\')';
                            } else {
                                $param_key = $this->paramIsExit($key, $this->param);
                                $this->filter .= ' AND `' . $key . '` ' . $value[0] . ' :' . $param_key;
                                $this->param[$param_key] = $value[1];
                            }
                        } else {
                            $this->filter .= ' AND `' . $key . '` = :' . $key;
                            $this->param[$key] = $value;
                        }
                    }
                }
            }
        }
        return $this;
    }

    public function paramIsExit($key, $param)
    {
        if (!isset($param[$key])) {
            return $key;
        } else {
            $key .= $key;
            return $this->paramIsExit($key, $param);
        }
    }

    /**
     * left join
     * @param array $order 排序条件
     * @return $this
     * $order='id desc'
     */
    public function join($condition = '')
    {
        if ($condition) {
            $this->filter = ' LEFT JOIN ' . $condition . ' ';
        }
        return $this;
    }

    /**
     * 拼装排序条件
     * @param array $order 排序条件
     * @return $this
     * $order='id desc'
     */
    public function order($order = 'id desc')
    {
        if ($order) {
            $this->filter .= ' ORDER BY ' . $order . ' ';
        }
        return $this;
    }

    /**
     * group by
     * @param array $order
     * @return $this
     * $group=['sex','name']
     */
    public function group($group = [], $having = '')
    {
        if ($group) {
            $this->filter .= ' GROUP BY ';
            $this->filter .= ' ' . implode(' ,', $group) . ' ' . $having . ' ';
        }
        return $this;
    }

    /**
     * 查询limit
     * @param string
     * @return $this
     * $limit = '100'   限制查询100条
     * $limit = '2,100' 查询第二页 100条数据
     */
    public function limit($limit = '100')
    {
        $this->filter .= ' LIMIT ' . $limit . ' ';
        return $this;
    }

    /**
     * 查询字段
     * @param string $field
     * @return $this
     * $field = 'id,count(1),sub(num)'
     */
    public function field($field = '*')
    {
        $this->field = $field;
        return $this;
    }

    /**
     * 查询所有
     * @return mixed
     */
    public function fetchAll()
    {
        $sql = sprintf('SELECT %s FROM `%s` %s', $this->field, $this->table, $this->filter);
        $this->filter = '';
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, $this->param);
        $this->param = [];
        $sth->execute();

        return $sth->fetchAll();
    }

    /**
     * 查询一条
     * @return mixed
     */
    public function fetch()
    {
        $sql = sprintf('SELECT %s FROM `%s` %s LIMIT 1', $this->field, $this->table, $this->filter);
        $this->filter = '';
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, $this->param);
        $this->param = [];
        $sth->execute();

        return $sth->fetch();
    }

    /**
     * 根据条件（id）删除
     * @param $id
     * @return mixed
     */
    public function delete()
    {
        $sql = sprintf('DELETE FROM `%s` %s LIMIT 1', $this->table, $this->filter);
        $this->filter = '';
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, $this->param);
        $this->param = [];
        $sth->execute();

        return $sth->rowCount();
    }

    /**
     * 新增数据
     * @param $data
     * @return mixed
     */
    public function add($data)
    {
        $sql = sprintf('INSERT INTO `%s` %s', $this->table, $this->formatInsert($data));
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, $data);
        $sth->execute();

        return $sth->rowCount();
    }

    /**
     * 新增多条数据
     * @param $data
     * @return int
     */
    public function addAll($data)
    {
        $sql = sprintf('INSERT INTO `%s` %s', $this->table, $this->formatInsertAll($data));
        $sth = Db::pdo()->prepare($sql);
        $sth->execute();

        return $sth->rowCount();
    }

    /**
     * 修改数据
     * @param $data
     * @return mixed
     */
    public function update($data)
    {
        $sql = sprintf('UPDATE `%s` SET %s %s', $this->table, $this->formatUpdate($data), $this->filter);
        $this->filter = '';
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatUpdateParam($sth, $data);
        $sth = $this->formatParam($sth, $this->param);
        $this->param = [];
        $sth->execute();

        return $sth->rowCount();
    }

    /**
     * 绑定具体变量值
     * @param PDOStatement $sth
     * @param array $params
     * @return PDOStatement
     */
    public function formatParam(PDOStatement $sth, $params = [])
    {
        foreach ($params as $param => &$value) {
            $param = is_int($param) ? $param + 1 : ':' . ltrim($param, ':');
            $sth->bindParam($param, $value);
        }
        return $sth;
    }

    /**
     * 绑定具体变量值update
     * @param PDOStatement $sth
     * @param array $params
     * @return PDOStatement
     */
    public function formatUpdateParam(PDOStatement $sth, $params = [])
    {
        foreach ($params as $param => &$value) {
            $param = ':' . ltrim($param, ':').'update';
            $sth->bindParam($param, $value);
        }
        return $sth;
    }
    /**
     * 将数组转化为插入格式的sql语句
     * @param $data
     * @return string
     */
    private function formatInsert($data)
    {
        $fields = [];
        $names = [];
        foreach ($data as $key => $value) {
            $fields[] = sprintf('`%s`', $key);
            $names[] = sprintf(':%s', $key);
        }
        $field = implode(',', $fields);
        $name = implode(',', $names);
        return sprintf('(%s) VALUES(%s)', $field, $name);
    }

    /**
     * 格式化插入数据
     * @param $data
     * @return string
     */
    private function formatInsertAll($data)
    {
        $fields = [];
        $values = '';
        foreach ($data[0] as $key => $value) {
            $fields[] = sprintf('`%s`', $key);
        }
        foreach ($data as $k => $v) {
            $values .= '(\'' . implode('\',\'', $v) . '\'),';
        }
        $field = implode(',', $fields);
        $values = rtrim($values, ',');
        return sprintf('(%s) VALUES %s', $field, $values);
    }

    /**
     * 更新数据的格式转换
     * @param $data
     * @return string
     */
    private function formatUpdate($data)
    {
        $fields = [];
        foreach ($data as $key => $value) {
            $fields[] = sprintf('`%s`=:%s', $key, $key.'update');
            $this->param[$key.'update'] = $value;
        }
        return implode(',', $fields);
    }
}
