<?php


namespace app\common\command;


use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use think\Db;

/**
 * 安装命令
 * Class Install
 * @package app\common\command
 */
class Install extends Command
{
    protected function configure()
    {
        $this->setName('install')
            ->addOption('name', NULL, Option::VALUE_OPTIONAL, "database connect args like  username:password@dataname or username:password@host:dataname")
            ->addOption('file', 'f', Option::VALUE_OPTIONAL, "script file name")
            ->addOption('module', 'm', Option::VALUE_REQUIRED, 'install extend module ex. shop')
            ->addOption('username', 'u', Option::VALUE_OPTIONAL, "Specify the super admin account")
            ->addOption('password', 'p', Option::VALUE_REQUIRED, 'Specify the super admin password')
            ->setDescription('Install db script');
    }

    /**
     * 执行安装
     * @param Input $input
     * @param Output $output
     * @return void
     */
    protected function execute(Input $input, Output $output)
    {
        $lockfile = app()->getRuntimePath() . 'install.lock';
        if (file_exists($lockfile)) {
            $output->error('The system has been installed. If you want to reinstall, please delete the file ' . $lockfile . ' and run this command again.');
            return;
        }

        $dbconfig = config('database.');

        if ($input->hasOption('name')) {
            $name = trim($input->getOption('name'));
            $args = explode('@', $name);
            if (empty($args) || count($args) < 2 || strpos(':', $args[0]) === false) {
                $output->writeln("Install aborted with error: arguments error.");
                exit;
            }
            list($username, $password) = explode(':', $args[0]);
            $dbconfig['username'] = $username;
            $dbconfig['password'] = $password;

            if (strpos(':', $args[1]) === false) {
                $dbname = $args[1];
            } else {
                list($host, $dbname) = explode(':', $args[1]);
                $dbconfig['hostname'] = $host;
            }
            $dbconfig['database'] = $dbname;
        }

        //install code
        if ($input->hasOption('sql')) {
            $sql = './' . trim($input->getOption('sql')) . '.sql';
            if (!file_exists($sql)) {
                $output->error('The specified sql script not exists.');
                return;
            }
            $this->runsql($sql, $dbconfig['prefix']);
        } else {
            $path = app()->getAppPath();
            $sqlpath = $path . '../dbscript';
            if (!is_dir($sqlpath)) {
                $sqlpath = $path . '../../dbscript';
            }
            if (!is_readable($sqlpath)) {
                $output->error('Please ensure the dbscript folder exists and accessable.');
                return;
            }
            if (!is_dir($sqlpath)) {
                $output->error('Please upload the dbscript folder or specify sql option.');
                return;
            } else {
                foreach (['struct', 'init'] as $script) {
                    $sql = $sqlpath . '/' . $script . '.sql';
                    if (file_exists($sql)) {
                        $output->writeln("Run sql $script.sql.");
                        $this->runsql($sql, $dbconfig['prefix']);
                    } else {
                        $output->warning('Sql file ' . $script . '.sql not exists!');
                    }
                }
                if ($input->hasOption('module')) {
                    $mode = $input->getOption('module');
                    if (!empty($mode)) {
                        $modes = explode(',', $mode);
                        foreach ($modes as $mode) {
                            $sql = $sqlpath . '/update_' . $mode . '.sql';
                            if (file_exists($sql)) {
                                $output->writeln("Install module $mode.");
                                $this->runsql($sql, $dbconfig['prefix']);
                            } else {
                                $output->warning('Sql file update_' . $mode . '.sql not exists!');
                            }
                        }
                    }
                }
            }
        }

        $data = [];
        if ($input->hasOption('username')) {
            $data['username'] = $input->getOption('username');
        }

        if ($input->hasOption('password')) {
            $password = $input->getOption('password');
            $data['salt'] = random_str(8);
            $data['password'] = encode_password($password, $data['salt']);
        }
        if (!empty($data)) {
            $output->writeln("Reset manager.");
            Db::name('Manager')->where('id', 1)->update($data);
        }

        file_put_contents($lockfile, time());

        $output->writeln("Installation successful.");
    }

    protected function runsql($file, $prefix)
    {
        $sqls = $this->explodesql($file, 'sa_', $prefix);
        foreach ($sqls as $sql) {
            Db::execute($sql);
        }
    }

    protected function explodesql($sql_path, $old_prefix = "", $new_prefix = "", $separator = ";\n")
    {
        $commenter = array('#', '--');
        //判断文件是否存在
        if (!file_exists($sql_path))
            return false;

        $content = file_get_contents($sql_path);   //读取sql文件
        $content = str_replace(array('`' . $old_prefix, ' ' . $old_prefix, "\r"), array('`' . $new_prefix, ' ' . $new_prefix, "\n"), $content); //替换前缀

        //通过sql语法的语句分割符进行分割
        $segment = explode($separator, trim($content));

        //去掉注释和多余的空行
        $data = array();
        foreach ($segment as  $statement) {
            $sentence = explode("\n", $statement);
            $newStatement = array();
            foreach ($sentence as $subSentence) {
                if ('' != trim($subSentence)) {
                    //判断是会否是注释
                    $isComment = false;
                    foreach ($commenter as $comer) {
                        if (preg_match("/^(" . $comer . ")/is", trim($subSentence))) {
                            $isComment = true;
                            break;
                        }
                    }
                    //如果不是注释，则认为是sql语句
                    if (!$isComment)
                        $newStatement[] = $subSentence;
                }
            }
            $data[] = $newStatement;
        }

        //组合sql语句
        foreach ($data as  $statement) {
            $newStmt = '';
            foreach ($statement as $sentence) {
                $newStmt = $newStmt . trim($sentence) . "\n";
            }
            if (!empty($newStmt)) {
                $result[] = $newStmt;
            }
        }
        return $result;
    }
}
