<?php

namespace xmvc;

/**
* 数据库类
*
* @version 2.0.0
*/

class DB extends PdoHelper
{
    /**当前 Sqler*/
    protected Sqler|null $currentSqler = null;
    /**
     * 构造
     *
     * @param array|string  $key 配置项中的 net.db.<key>
     */
    public function __construct(array|string $cfg = 'main')
    {
        if (is_string($cfg)) {
            if (!isset(MVC::$cfg['database'])) {
                MVC::$cfg['database'] = loadConfig('database');
            }
            if (!isset(MVC::$cfg['database'][$cfg])) {
                error('未定义的数据库配置 ' . $cfg);
            }
            parent::__construct($cfg, MVC::$cfg['database'][$cfg]);
        } else {
            $key = $cfg['hostname'] . ':' . $cfg['hostport'] . '@' . $cfg['database'] . '-' . md5(serialize($cfg));
            parent::__construct($key, $cfg);
        }
    }

    /**
     * 创建一个 Sqler 对象
     *
     * @return Sqler
     */
    public function newSqler(): Sqler
    {
        $this->currentSqler = new Sqler($this);
        return $this->currentSqler;
    }

    /**
     * 返回当前 Sqler 对象
     *
     * @return Sqler|null
     */
    public function getSqler(): Sqler|null
    {
        return $this->currentSqler;
    }

    /**
     * 改变当前 currentSqler 的 Sqler 对象
     *
     * @return self
     */
    public function changeSqler(Sqler $helper = null): self
    {
        if (!is_null($helper)) {
            $this->currentSqler = $helper;
        } elseif (!isset($this->currentSqler)) {
            error(lg('db->currentSqler 为 null, 无法构建 SQL 语句'));
        }
        return $this;
    }

    /**
     * 事务 Transaction
     *
     * @param Closure $func,  function($q, $db);
     * - $q = 当前调用的 db 类->createSqler()
     * - $db = 当前调用的 db 类
     * @return mixed $func() 返回的结果
     */
    public function transaction(\Closure $func): mixed
    {
        $this->begin();
        $result = $func($this->newSqler(), $this);
        $this->end();
        return $result;
    }

    /**
     * 绑定查询构建器 Sqler
     *
     * - 绑定查询Helper并执行查询语句,准备好 statement 供 all,row,one 使用。
     * @param Sqler $helper 默认为null, 为当前 currentSqler
     * @return self
     */
    public function select(Sqler $helper = null): self
    {
        $this->changeSqler($helper);
        $this->query($this->currentSqler->getSelectSQL(), $this->currentSqler->getParams());
        return $this;
    }

    /**
     * 取查询结果所有行
     *
     * @return array
     */
    public function all(): array
    {
        $rows = parent::all();
        if (!is_null($this->currentSqler) && !is_null($this->currentSqler->mapFetchKeyValue)) {
            $data = [];
            if (!empty($rows)) {
                list($keyField, $valueField, $func) = $this->currentSqler->mapFetchKeyValue;
                $data = map($rows, $keyField, $valueField, $func);
            }
            return $data;
        } else {
            return $rows;
        }
    }

    public function has(Sqler $helper = null): bool
    {
        $this->changeSqler($helper);
        return !empty($this->query($this->currentSqler->getHasSQL())->one());
    }

    public function count(Sqler $helper = null): int
    {
        $this->changeSqler($helper);
        return $this->query($this->currentSqler->getCountSQL())->one();
    }


    /**
     * 构建多条件插入语句
     *
     * @param Sqler $helper 如果为空则默认当前(也就是最近一次操作的) Sqler
     * @param int $chunkQty array_chunk 按多少条数来分割
     * @return array 多批插入SQL
     */
    public function insert(Sqler $helper = null, int $chunkQty = 200): int
    {
        $this->changeSqler($helper);
        if ($this->currentSqler->hasLots()) {
            return $this->insertLots($this->currentSqler, $chunkQty);  //批量
        } else {
            return $this->exec($this->currentSqler->getInsertSQL(), $this->currentSqler->getParams());
        }
    }

    /**
     * 构建插入语句 - 批量
     *
     * @param int $chunkQty array_chunk 按多少条数来分割
     * @return int 返回插入的记录数量
     */
    public function insertLots(Sqler $helper = null, int $chunkQty = 200): int
    {
        $this->changeSqler($helper);
        $sqlArr = $this->currentSqler->getInsertLotsSQL($chunkQty);
        $num = 0;
        if ($sqlArr) {
            foreach ($sqlArr as $sql) {
                $num += $this->exec($sql, $this->currentSqler->getParams());
            }
        }
        return $num;
    }

    /**
     * 构建完全字段替换或插入语句(原生 REPLACE INTO)
     */
    public function replaceInto(Sqler $helper = null): int
    {
        $this->changeSqler($helper);
        return $this->exec($this->currentSqler->getReplaceIntoSQL());
    }

    /**
     * 构建检测字段替换或插入语句, 值相同不更新，值不同则更新，没有找到则新增
     */
    public function replaceCheck(Sqler $helper = null): int
    {
        $this->changeSqler($helper);
        if (!$this->currentSqler->hasWhere()) {
            throw new \Exception("构建SQL失败,缺失条件语句，须调用 Sqler->where() 方法");
        }
        if ($this->has($this->currentSqler)) {
            return $this->exec($this->currentSqler->getUpdateSQL());
        } else {
            return $this->exec($this->currentSqler->getInsertSQL());
        }
    }

    public function update(Sqler $helper = null): int
    {
        $this->changeSqler($helper);
        return $this->exec($this->currentSqler->getUpdateSQL());
    }

    public function delete(Sqler $helper = null): int
    {
        $this->changeSqler($helper);
        return $this->exec($this->currentSqler->getDeleteSQL());
    }

    public function truncate(Sqler $helper = null): int
    {
        $this->changeSqler($helper);
        return $this->exec($this->currentSqler->getTruncateSQL());
    }
}
