<?php
declare (strict_types=1);

namespace app\admin\controller;

use Exception;
use think\facade\Db;
use think\facade\Config;
use think\facade\Request;
use think\facade\View;
use app\common\core\core;
use ZipArchive;
use app\common\util\Upload as Up;

class Update extends Base
{
    protected $middleware = ['AdminCheck', 'AdminPermission'];

    //进入更新页面
    public function index()
    {
        $update = self::check_ver();
        $update_info = Core::updateInfo();
        $ver = env('YuanVer');
        
        View::assign([
            'update' => $update,
            'update_info' => $update_info,
            'ver' => $ver
        ]);
        return $this->fetch();
    }

    //检查是否有更新
    public function check_ver()
    {
        $core = new Core();
        $res = $core->UpCheck(env('YuanVer'));
        $res = json_decode($res, true);
        if (empty($res['vertime'])) {
            $vertime = date('Y-m-d');
        } else {
            $vertime = date('Y-m-d', $res['vertime']);
        }
        if ($res['code'] == 201) {
            return ['code' => $res['code'], 'msg' => '当前已是最新版本!', 'version' => $res['vername'], 'vertime' => $vertime];
        }
        if ($res['code'] == 200) {
            return ['code' => 200, 'msg' => '获取成功', 'vertime' => $vertime, 'version' => $res['version'], 'updateMsg' => $res['updateMsg']];
        }

        return ['code' => 202, 'msg' => $res['msg']];
    }

    public function checkver()
    {
        $core = new Core();
        $res = $core->UpCheck(env('YuanVer'));
        $res = json_decode($res, true);
        if (empty($res['vertime'])) {
            $vertime = date('Y-m-d');
        } else {
            $vertime = date('Y-m-d', $res['vertime']);
        }
        if ($res['code'] == 201) {
            return $this->getJson([
                'code' => 201, 
                'msg' => '当前已是最新版本!', 'data' => [
                    'version' => $res['vername'],
                    'vertime' => $vertime
                ]
            ]);
        }
        if ($res['code'] == 200) {
            return $this->getJson([
                'code' => 200, 'msg' => '获取成功', 'data' => [
                    'vertime' => $vertime,
                    'version' => $res['version'],
                    'updateMsg' => $res['updateMsg']
                ]
            ]);
        }
        return $this->getJson(['code' => 202, 'msg' => $res['msg']]);
    }


    //执行更新
    public function update_ver()
    {
        $core = new Core();
        $res = $core->UpCheck(env('YuanVer'));
        $res = json_decode($res, true);
        if ($res['code'] == 201) {
            return $this->getJson(['code' => 201, 'msg' => $res['msg']]);
        } else {
            $file_url = $res['downurl'];
            $filename = basename($file_url);
            $dir = app()->getRootPath() . "runtime/upgrade/";
            if (!file_exists($dir)) {
                mkdir($dir, 0777, true);
            }
            $path = file_exists($dir . $filename) ? $dir . $filename : $this->download_file($file_url, $dir, $filename);

            $zip = new ZipArchive();
            //打开压缩包
            if ($zip->open($path) === true) {
                $toPath = app()->getRootPath();
                try {
                    //解压文件到toPath路径下，用于覆盖差异文件
                    $zip->extractTo($toPath);
                    //判断文件是否存在
                    if (file_exists($toPath . '/upDelete.txt')) {
                        //读取删除文件文本
                        $fp = fopen($toPath . '/upDelete.txt', 'r');
                        $upDelete = fread($fp, filesize($toPath . '/upDelete.txt'));
                        @eval("\$array = $upDelete;");//将文本内容转为数组格式
                        foreach ($array as $value) {
                            $dir = $toPath . $value;
                            //判断是目录还是文件
                            if (is_dir($dir)) {
                                //调用公共删除文件方法
                                delete_dir($dir);
                            } else {
                                if (file_exists($dir)) {
                                    unlink($dir); //删除不需要的文件
                                }
                            }

                        }
                        fclose($fp);
                        unlink($toPath . '/upDelete.txt'); //删除-删除文件文本
                    }
                    unlink($path); //删除更新包

                } catch (Exception $e) {
                    return $this->getJson(["msg" => $e->getMessage(), "code" => 201]);
                }
                //文件差异覆盖完成，开始更新数据库
                //执行数据库
                $dbpk = '';
                $dbhost = Config::get('database.connections.mysql.hostname');
                $dbport = Config::get('database.connections.mysql.hostport');
                $dbname = Config::get('database.connections.mysql.database');
                $dsn = "mysql:host=$dbhost:$dbport;dbname=$dbname";
                $db = new \PDO($dsn, Config::get('database.connections.mysql.username'), Config::get('database.connections.mysql.password'));

                $list = scandir($_SERVER['DOCUMENT_ROOT'] . '/../app/update');
                // 文件头两个是 . 和 .. 要去掉
                unset($list[0]);
                unset($list[1]);
                
                
                
                // 获取当前数据库版本号
                $db_version = Db::name('admin_config')->where(['config_name' => 'db_version'])->find();

                $last = '';
                foreach ($list as $item) {
                    $tmp = str_replace('.sql', '', $item);
                    
                    if ((int)$tmp > (int)$db_version['config_value']) {
                        self::createTables($db, $dbpk, $_SERVER['DOCUMENT_ROOT'] . '/../app/update/' . $tmp . '.sql');
                    }

                    $last = $tmp;
                }
                // 将最后一次更新的版本号记录到数据库
                if (!$db_version) {
                    Db::name('admin_config')->insert([
                        'config_name' => 'db_version',
                        'config_value' => $last,
                    ]);
                } else {
                    Db::name('admin_config')->where(['config_name' => 'db_version'])->save([
                        'config_name' => 'db_version',
                        'config_value' => $last,
                    ]);
                }

                return $this->getJson(['code' => 1, 'msg' => '版本更新成功请刷新缓存!']);
            } else {
                unlink($path); //删除更新包
                return $this->getJson(["msg" => "更新包解压失败，请重试！", "code" => 0]);
            }
        }
    }

    //压缩包文件下载
    public function download_file($url, $dir, $filename = '')
    {
        if (empty($url)) {
            return false;
        }
        $ext = strrchr($url, '.');
        $dir = realpath($dir);
        //目录+文件
        $filename = (empty($filename) ? '/' . time() . '' . $ext : '/' . $filename);
        $filename = $dir . $filename;
        //开始捕捉
        ob_start();
        readfile($url);
        $img = ob_get_contents();
        ob_end_clean();
        $size = strlen($img);
        $fp2 = fopen($filename, "a");
        fwrite($fp2, $img);
        fclose($fp2);
        return $filename;
    }

    private function createTables($db, $pk, $sql_file = '')
    {
        $sql = str_replace(
            ['{{$pk}}'],
            [$pk],
            file_get_contents($sql_file)
        );
        $sql_array = preg_split("/;[\r\n]+/", $sql);
        foreach ($sql_array as $k => $v) {
            if (!empty($v)) {
                try {
                    if (substr($v, 0, 12) == 'CREATE TABLE') {
                        $name = preg_replace("/^CREATE TABLE `(\w+)` .*/s", "\\1", $v);
                        $msg = "创建数据表{$name}";
                        $res = $db->query($v);
                        if ($res == false) {
                            return $msg . '失败';
                        }
                    } else {
                        $res = $db->query($v);
                        if ($res == false) {
                            return '数据插入失败';
                        }
                    }
                } catch (Exception $exception) {

                }
            }
        }
        return false;
    }
    
    //上传解压更新包
    public static function upload(){
        return json(Up::updatePutFile(Request::file('file')));
    }
}
