<?php
/**
 * 安装配置
 * ============================================================================
 * 版权所有 2017北京素玄科技，并保留所有权利。
 * 
 * 网站地址: http://www.suxuantech.com
 * ----------------------------------------------------------------------------
 * 这不是一个自由软件！未经允许的情况下，您不能对本系统代码做任何修改 .
 * 不允许对程序代码以任何形式任何目的的再发布。
 * 如有修改需求，请联系素玄科技有限公司：contact@suxuantech.cn
 * ============================================================================
 * $Author: songdemei<songdemei@suxuantech.cn> 2018-5-23 $
 */
namespace app\index\controller;
use PDO;
use PDOStatement;
use think\Controller;
use think\Db;


class Install extends Controller{
    public function index(){
        $this->checkWriteable();
        config('app_debug',true);
        $data = $this->initConfig();
        if(!$data['init'] && !session('sx_admin')){
            return $this->redirect('login');
        }
        $data['ext']['app_secret'] = config('ext.app_secret')?config('ext.app_secret'):md5(get_rand_string());
        $mcUrl = config('ext.app_config')['sx_mc']['addr'];
        $mcUrl = $mcUrl?$mcUrl:request()->domain();
        $data['ext']['mc_url'] = $mcUrl;
        $database = $data['database'];
        // if($database['deploy'] == 1 && $database['rw_separate'] === true){
        $databaseHost = explode(',', $database['hostname']);
        if(count($databaseHost) > 1){
            $database['hostname'] = $databaseHost[0];
            unset($databaseHost[0]);
            $database['hostname2'] = implode(',', $databaseHost);
        }
        //}
        if($data['init'] && $database['hostname'] && $database['username']){
            $dbList = $this->getDbList(['hostname'=>$database['hostname'],'username'=>$database['username'],'password'=>$database['password'],'hostport'=>$database['hostport']]);
            $this->assign('db_list',$dbList);
            if($data['init'] == 1){
                //初始安装
                $data['account'] = [
                    'username'=>'root',
                    'password'=>get_rand_string(8,1),
                ];
            }
        }
        $data['database'] = $database;

        $dblogs = $data['db_logs'];
        $databaseHost = explode(',', $dblogs['hostname']);
        if(count($databaseHost) > 1){
            $dblogs['hostname'] = $databaseHost[0];
            unset($databaseHost[0]);
            $dblogs['hostname2'] = implode(',', $databaseHost);
        }

        if($data['init'] && $dblogs['hostname'] && $dblogs['username']){
            $dbLogsList = $this->getDbList(['hostname'=>$dblogs['hostname'],'username'=>$dblogs['username'],'password'=>$dblogs['password'],'hostport'=>$dblogs['hostport']]);
            $this->assign('db_logs_list',$dbLogsList);
        }
        $data['db_logs'] = $dblogs;
        $data['product_id'] = getProductId();

        
        $this->assign('data',$data);
        return $this->fetch();
    }
    public function save(){

        $return = ['code'=>200,'msg'=>'','data'=>''];
        $post = input('post.');
        //var_dump($post);
        if(empty($post['ext'])|| empty($post['ext']['mc_url'])){
            $return['code'] = 201;
            $return['msg'] = 'MC地址不可以为空。';
            return json($return);die;
        }
        if(empty($post['database'])||empty($post['database']['hostname']) || empty($post['database']['username'])){
            $return['code'] = 202;
            $return['msg'] = '主数据库配置不可为空。';
            return json($return);die;
        }
        if(empty($post['db_logs'])||empty($post['db_logs']['hostname']) || empty($post['db_logs']['username'])){
            $return['code'] = 203;
            $return['msg'] = '日志数据库配置不可为空。';
            return json($return);die;
        }

        if(empty($post['product_id'])){
            $return['code'] = 204;
            $return['msg'] = '产品注册码不可为空。';
            return json($return);die;
        }
        /***************与MC进行握手**************/




        /************* 主 db config *************/
        $initConfig = $this->initConfig();
        $database = $post['database'];
        unset($post['database'],$initConfig['database']);//不在config内的key都要删除
        $res = $this->getDbList(['hostname'=>$database['hostname'],'username'=>$database['username'],'password'=>$database['password'],'hostport'=>$database['hostport']]);
        if(!$res){
            $return['code'] = 301;
            $return['msg'] = '主数据库无法连接，请检查数据库配置。';
            return json($return);die;
        }
        if(!in_array($database['database'],$res)){
            $return['code'] = 302;
            $return['msg'] = '主数据库没有：'.$database['database'].'数据库，请创建并给予相应用户权限。';
            return json($return);die;
        }
        if(!empty(trim($database['hostname2']))){
            $host2 = explode(',', $database['hostname2']);
            foreach ($host2 as $k => $v) {
                $res = $this->getDbList(['hostname'=>$v,'username'=>$database['username'],'password'=>$database['password'],'hostport'=>$database['hostport']]);
                if(!$res){
                    $return['code'] = 303;
                    $return['msg'] = '主数据库从库：'.$v.',无法连接，请检查数据库配置。';
                    return json($return);die;
                }
            }
            unset($host2);
        }

        /************* 日志 db config *************/
        $dbLogs = $post['db_logs'];
        $res = $this->getDbList(['hostname'=>$dbLogs['hostname'],'username'=>$dbLogs['username'],'password'=>$dbLogs['password'],'hostport'=>$dbLogs['hostport']]);
        if(!$res){
            $return['code'] = 311;
            $return['msg'] = '日志数据库配置有问题。';
            return json($return);die;
        }
        if(!in_array($dbLogs['database'],$res)){
            $return['code'] = 312;
            $return['msg'] = '日志库没有：'.$dbLogs['database'].'数据库，请创建并给予相应用户权限。';
            return json($return);die;
        }
        if(!empty(trim($dbLogs['hostname2']))){
            $host2 = explode(',', $dbLogs['hostname2']);
            foreach ($host2 as $k => $v) {
                $res = $this->getDbList(['hostname'=>$v,'username'=>$dbLogs['username'],'password'=>$dbLogs['password'],'hostport'=>$dbLogs['hostport']]);
                if(!$res){
                    $return['code'] = 313;
                    $return['msg'] = '日志数据库从库：'.$v.',无法连接，请检查数据库配置。';
                    return json($return);die;
                }
            }
            unset($host2);
        }
        $post['db_logs'] = array_merge($initConfig['db_logs'],$dbLogs);
        unset($dbLogs);
        /*************** REDIS测试配置  **********************/
        if($post['redis']['host']){
            $redis = $post['redis'];
            $redis['port'] = $redis['port']?$redis['port']:6379;
            $configTmp = [
                // 驱动方式
                'type'   => 'Redis',
                // 缓存保存目录
                'host'   => $redis['host'],
                // 缓存前缀
                'port'=>$redis['port'],
                'select'=>$redis['select'],
                // 缓存有效期 0表示永久缓存--测试，60秒后过期
                'expire' => 60,
                'timeout'=>5,
            ];
            try{
                cache('test_install_redis','1',$configTmp);
            }catch(\Exception $e){
                $return['code'] = 401;
                $return['msg'] = 'Redis队列连接失败，请检查参数。';
                return json($return);die;
            }
            
            //$res = cache('?test_install_redis','',$configTmp);
            $post['redis'] = array_merge($initConfig['redis'],$redis);
            unset($redis);
        }else{
            unset($post['redis']);
        }
        if($post['session']['host']){
            $session = $post['session'];
            $session['port'] = $session['port']?$session['port']:6379;
            $session['type'] = 'Redis';
            $configTmp = [
                // 驱动方式
                'type'   => 'Redis',
                // 缓存保存目录
                'host'   => $session['host'],
                // 缓存前缀
                'port'=>$session['port'],
                'select'=>$session['select'],
                // 缓存有效期 0表示永久缓存--测试，60秒后过期
                'expire' => 60,
                'timeout'=>5,
            ];
            try{
                cache('test_install_session','1',$configTmp);
            }catch(\Exception $e){
                $return['code'] = 402;
                $return['msg'] = 'Session服务连接失败，请检查参数。';
                return json($return);die;
            }
            $post['session'] = array_merge($initConfig['session'],$session);
            unset($session);
        }else{
            unset($post['session']);
        }
        if($post['cache']['host']){
            $cache = $post['cache'];
            $cache['port'] = $cache['port']?$cache['port']:6379;
            $cache['type']='Redis';
            $configTmp = [
                // 驱动方式
                'type'   => 'Redis',
                // 缓存保存目录
                'host'   => $cache['host'],
                // 缓存前缀
                'port'=>$cache['port'],
                'select'=>$cache['select'],
                // 缓存有效期 0表示永久缓存--测试，60秒后过期
                'expire' => 60,
                'timeout'=>5,
            ];
            try{
                cache('test_install_cache','1',$configTmp);
            }catch(\Exception $e){
                $return['code'] = 402;
                $return['msg'] = 'Cache服务连接失败，请检查参数。';
                return json($return);die;
            }
            $post['cache'] = array_merge($initConfig['cache'],$cache);
            unset($cache);
        }else{
            unset($post['cache']);
        }
        
        /***************** 产品注册 ********************/
        $lib = new \app\index\lib\Lib();
        $auth = $lib->getProductAuthFile();
        if(!$auth){
            $error = $lib->getError();
            $return['msg'] = '获取授权信息出错，错误信息：('.$error['errid'].')'.$error['error'];
            $return['code'] = 502;
            return json($return);die;
        }
        unset($post['product_id']);
        $authInfo = $lib->get_auth_key();
        //var_dump($authInfo);
        $shop = explode('*', $authInfo['auth'][1]);
        $return['msg'] = '<br/>授权到期时间：'.$authInfo['auth'][0]."<br/>"."授权门店数：".count($shop);
        unset($authInfo,$shop);
        /*************************************/

        /*********** 保存database 配置 ***************/
        $database = $this->mergeDbconfig($database);
        $dbContent = $this->arrayToString($database);
        file_put_contents(APP_PATH.DS.'database.php', $dbContent);

        /*************** 保存 EXT文件**********************/
        $ext = [
            'app_secret'=>$post['ext']['app_secret'],
            'app_config'=>[
                'sx_mc'=>[
                    'addr'=>$post['ext']['mc_url'],
                    ],
            ],
        ];

        $extContent = $this->arrayToString($ext);
        file_put_contents(APP_PATH.DS.'extra'.DS.'ext.php', $extContent);
        if($initConfig['ext'] != $post['ext']['app_secret']){
            $tmp = [
                'app_secret'=>$post['ext']['app_secret'],
            ];
            $res = db('api_app',$database)->where('1=1')->update($tmp);
            if($res === false){
                $return['msg'] = '更新数据库通迅密钥出错，请检查数据库配置后重试。';
                $return['code'] = 601;
            }
        }
        unset($post['ext'],$initConfig['ext']);
        

        

        /*********** DB 帐号 ***************/

        if($post['account']['username'] && $post['account']['password']){
            $tmp = [
                'username'=>$post['account']['username'],
                'password'=>user_md5($post['account']['password'],$initConfig['AUTH_KEY']),
            ];
            $res = db('api_user',$database)->where('id',1)->update($tmp);
            if($res === false){
                $return['msg'] = '保存管理员帐号出错，请检查主数据库帐号是否有更新数据库权限';
                $return['code'] = 601;
            }
        }

        unset($post['account'],$initConfig['init']);

        $config = array_merge($initConfig,$post);
        $configContent = $this->arrayToString($config);
        file_put_contents(APP_PATH.DS.'config.php', $configContent);
        return json($return);die;
    }

    public function saveProduct(){
        $return = ['code'=>200,'msg'=>'','data'=>''];
        $productId = input('product_id');
        $currentProductId = getProductId();
        if($productId != $currentProductId){
            $reg = ['product_id'=>$productId];
            $productIdContent = $this->arrayToString($reg);
            $fileName = ROOT_PATH.DS.'runtime'.DS.'Suxuan.php';
            $res = file_put_contents($fileName, $productIdContent);
            if(!$res){
                $return['msg'] = '更新授权文件出错，请确保runtime目录可写';
                $return['code'] = 501;
                return json($return);die;
            }else{
                return json($return);die;
            }
        }
        return json($return);die;
    }
    public function checkDb(){
        $hostname = input('hostname');
        $username = input('username');
        $password = input('password');
        $port = input('hostport');
        $res = $this->getDbList(['hostname'=>$hostname,'username'=>$username,'password'=>$password,'hostport'=>$port]);
        if($res === false){
            return json(['code'=>500,'msg'=>'数据库连接失败，请检查配置或权限']);
        }
        if(is_array($res) && empty($res)){
            return json(['code'=>501,'msg'=>'未找到数据库']);
        }
        return json(['code'=>200,'data'=>$res]);
    }
    /**
     * 将设置的config与默认配置进行合并
     * 
     * @param  [array] $config [设置的config]
     * @return [array]         [返回最终的数据库全的配置参数]
     */
    private function mergeDbconfig($config){
        if(file_exists(APP_PATH.DS.'database.php.example')){
            $default = include(APP_PATH.DS.'database.php.example');
        }else{
            $default = config()['database'];
        }
        if(trim($config['hostname2'])){
            $config['hostname'] = $config['hostname'].','.$config['hostname2'];
            unset($config['hostname2']);
            $default['deploy'] = 1;
        }
        unset($config['hostname2']);
        $default['rw_separate'] = true;
        $config = array_merge($default, $config);
        return $config;
    }
    private function initConfig(){
        $config = config();
        if(!file_exists(APP_PATH.DS.'config.php') && file_exists(APP_PATH.DS.'config.php.example')){
            $example = include(APP_PATH.DS.'config.php.example');
            $config = array_merge($config,$example);
        }
        if(!file_exists(APP_PATH.DS.'database.php') && file_exists(APP_PATH.DS.'database.php.example')){
            $example = include(APP_PATH.DS.'database.php.example');
            $config['database'] = array_merge($config['database'],$example);
            
        }
        if(!file_exists(ROOT_PATH.DS.'runtime'.DS.'Suxuan.php')){
            $config['init'] = 1;
        }
        return $config;
    }
    public function login(){
        return $this->fetch();
    }
    private function getDbList($params){
        $config = $this->mergeDbconfig($params);
        //配置时，超时时间设置短点。
        $config['params'] = [PDO::ATTR_TIMEOUT => 3];
        $config['database'] = null;
        $db = Db::connect($config);
        $sql = "show databases;";
        try{
            $res = $db->master()->query($sql);
        }catch(\Exception $e){
            return false;
        }
        
        $databases= [];
        foreach ($res as $key => $value) {
            if($value['Database']){
                $databases[] = $value['Database'];
            }
        }
        return $databases;
    }

    private function arrayToString($arr,$in=0,$sp="\t"){
        if(!is_array($arr)){
            return $arr;
        }
        if(empty($arr)){
            return '';
        }
        $res = $in?'':'<?php '."\r\n"."return [\r\n";
        foreach ($arr as $key => $value) {
            if(is_array($value)){

                $res .= $sp."'{$key}'=>[\r\n".$this->arrayToString($value,1,$sp."\t").$sp.'],'."\r\n";
            }else{
                if(!is_numeric($key)){
                    $key = "'".$key."'";
                }
                if(is_string($value)){
                    $value = str_replace('\\', '\\\\', $value);
                    $res .= $sp."{$key}=>'{$value}',\r\n";
                }elseif (is_int($value)) {
                    $res .= $sp."{$key}=>{$value},\r\n";
                }elseif (is_bool($value)) {
                    if($value === true){
                        $res .= $sp."{$key}=>true,\r\n";
                    }else{
                        $res .= $sp."{$key}=>false,\r\n";
                    }
                }else{
                    $res .= $sp."{$key}=>'{$value}',\r\n";
                }
            }
        }
        $res .= $in?"":'];';
        return $res;
    }
    private function checkWriteable(){
        $fileList = [
            ROOT_PATH . 'runtime',
            APP_PATH . 'extra',
            APP_PATH ,
            ROOT_PATH . 'public',
        ];
        $unwrite = '';
        foreach ($fileList as $key => $value) {
            if(!is_writeable($value)){
                $unwrite .= $value . "<br/>\r\n";
            }
        }
        if($unwrite){
            echo "以下文件不可写，请确保文件可写。<br/>\r\n";
            echo $unwrite;
            exit();
        }

    }

}