<?php

namespace xmvc\core;

use xmvc\MVC;
use xmvc\Dump;
use xmvc\Queue;
use xmvc\Viewer;
use xmvc\Encoder;
use xmvc\Session;
use xmvc\core\Power;

class Cmd
{
    /** 隐藏方法退出core登录 /xmvc/core/cmd-logout , 隐藏方法 logout() 通过 xmvc\core\power的验证方法实现 */

    /**
     * index
     */
    public function index()
    {
        $menus = [];
        $contents = file_get_contents(__FILE__);
        preg_match_all('/\/\*\*(.*?)\*\/\s+public\s+function\s+([^a-zA-Z0-9_]*)([a-zA-Z0-9_]+)(?=\()/s', $contents, $m);
        if ($m) {
            foreach ((array)$m[0] as $i => $v) {
                if (strpos($v, '@menu') !== false) {
                    $pattern = '/\/\*\*\s*\*\s*(.*?)\s*@/s';
                    if (preg_match($pattern, $v, $m2)) {
                        $remark = trim($m2[1], '*');
                    }
                    $funcName = $m[3][$i];
                    $reflector = new \ReflectionMethod($this, $funcName);
                    $reflectionParameters = $reflector->getParameters();
                    $params = [];
                    if ($reflectionParameters) {
                        foreach ($reflectionParameters as $k => $pv) {
                            preg_match('/@param[^\$]+\$' . $pv->name . '\s+([^\r|\n]+)/', $v, $m3);
                            $params[$k] = new \stdClass();
                            $params[$k]->name = $pv->name;
                            $params[$k]->remark = $m3 ? $m3[1] : $pv->name;
                        }
                    }
                    $menus[] = [$funcName, $remark, $params];
                }
            }
        }
        $liArr = [];
        foreach ($menus as $v) {
            list($funcName, $remark, $params) = $v;
            if ($params) {
                $inputs = [];
                foreach ($params as $k => $pv) {
                    $inputs[] = '<span style="font-size:.8em">' . $pv->remark . ':</span> <input type="text" style="width:100%" name="' . $pv->name . '">';
                }

                $liArr[] = '<li style="padding-top:.5em;font-size:1.1em;" class="border hover">'
                . $remark . '<br />'
                . implode('<br />', $inputs)
                . ' <br />
                <a href="' . HOST . '/xmvc/core/cmd-' . $funcName . '" onclick="return bindLiInputParamValue(this)" target="_blank" style="font-size:1.1em">执行</a></li>';
            } else {
                $liArr[] = '<li style="padding-top:.5em" class="border hover"><a href="' . HOST . '/xmvc/core/cmd-' . $funcName . '" target="_blank" style="font-size:1.1em">' . $remark . '</a></li>';
            }
        }
        $liHtml = '';
        $liHtml .= '<li><a href="' . HOST . '/xmvc/core/Cmd-logout" target="_blank" style="font-size:1.1em;color:#8066A0">安全退出</a></li>';
        $liHtml .= '<li><a href="' . URL_RUNTIME . '/runtime.html" target="_blank" style="font-size:1.1em;color:#8066A0">runtime 运行时状态</a></li>';
        $liHtml .= '<li class="list0"><a href="' . HOST . '/xmvc/core/cmd-showErrorLogFile~date-' . date('Y-m-d') . '" target="_blank" style="font-size:1.1em">错误日志 ( 今天, 只取前 1024 条记录 )</a></li>';
        $liHtml .= '<li class="list0"><a href="' . HOST . '/xmvc/core/cmd-clearErrorLogFile~date-' . date('Y-m-d') . '" target="_blank" style="font-size:1.1em;color:#C00" onclick="return confirm(\'确认要备份然后清空今天的日志?\')">备份今天的日志为 Y-m-d.log.backup 然后清空今天的日志文件</a></li>';
        $liHtml .= '<li>
            控制台
            <ul>
                <li style="padding-left:2em"><a href="' . URL_RUNTIME . '/cli_dump.html" target="_blank" style="font-size:1.1em">dump(),dd() 函数输出 ( HTML 版 )</a></li>
                <li style="padding-left:2em"><a href="' . URL_RUNTIME . '/cli_dump.json" target="_blank" style="font-size:1.1em">dump(),dd() 函数输出 ( JSON 版 )</a></li>
                <li style="padding-left:2em"><a href="' . URL_RUNTIME . '/cli_error.html" target="_blank" style="font-size:1.1em">error 错误输出 ( HTML版 )</a></li>
            </ul>
        </li>';
        $liHtml .= implode('', $liArr);
        $msg = '<div style="padding:1em"><ul>{:1}</ul></div>';
        $msg = qgprintf($msg, $liHtml);
        MVC::echo($msg, true, '<title>CMD - CORE</title>');
        exit();
    }

    /**
     * PHPINFO
     * @menu
     */
    public function phpinfo()
    {
        phpinfo();
        exit();
    }

    /**
     * 站点首次运行, 初始化一些内容(例缓存目录)
     * @menu
     */
    public function runFirst()
    {
        $indexFile = ROOT . '/index.php';
        $phpCode = file_get_contents($indexFile);
        //将index配置文件中的 FIRST_RUN 修改为 true
        $phpCode = preg_replace('/define\(([\s]+)?[\'|\"]FIRST_RUN[^;]+\;/is', "define('FIRST_RUN', true);", $phpCode);
        if ($phpCode) {
            file_put_contents_lock($indexFile, $phpCode);
        }
        echo '<script>fetch("' . HTTP_HOST . '")</script>';
        MVC::alert('执行完成', '首次初始化运行');
        exit();
    }

    /**
     * 显示 常量, 配置, $_SESSION, $_SERVER
     * @menu
     */
    public function cfg()
    {
        echo '<body style="padding:1em">';

        echo '<h1>常量</h1>';
        dump(get_defined_constants(true)['user']);

        echo '<h1>配置数组</h1>';
        //echo '<p>不含数据库配置</p>';
        $db = new \xmvc\DB();
        dump(MVC::$cfg);

        echo '<h1>_SESSION</h1>';
        if (isset($_SESSION)) {
            dump('注意存在 $_SESSION', $_SESSION);
        }
        dump(Session::getAll());

        echo '<h1>_SERVER</h1>';
        dump($_SERVER);
        exit();
    }

    /**
     * 配置文件
     * @menu
     */
    public function cfgUI()
    {
        require(DIR_MVC . '/core/cmd/' . __FUNCTION__ . '.php');
        exit();
    }

    /**
     * 生成所有视图缓存, 会先清空模板缓存目录
     * @menu
     */
    public function createAllTplCacheFile()
    {
        Viewer::createAllCacheFile();
        MVC::alert('生成完成', '生成所有视图缓存');
        exit();
    }

    /**
     * 更新静态文件版本号 - 使用当前 time()
     * @menu
     */
    public function updateSVER()
    {
        $indexFile = ROOT . '/index.php';
        $phpCode = file_get_contents($indexFile);
        $phpCode = preg_replace('/define\(([\s]+)?[\'|\"]SVER[^;]+\;/is', "define('SVER', " . time() . ");", $phpCode);
        if ($phpCode) {
            file_put_contents_lock($indexFile, $phpCode);
        }
        echo '<script>fetch("' . HTTP_HOST . '")</script>';
        MVC::alert('执行完成', '更新静态文件版本号');
        exit();
    }

    /**
     * 创建一个新的类, 使用 /xmvc/core/codeTpl/class.tpl.php 样板
     *
     * @param [string] $param 目标文件目录,base64编码
     */
    public function newClass(string $param)
    {
        $tmp = base64_decode($param);
        list($filename, $function) = explode('|', $tmp);
        if (file_exists($filename)) {
            error($filename . ' 类文件已存在');
        }
        $site = getSiteNameByDir($filename);
        $site = $site ? $site : '';
        $class = filenameNotExt($filename);
        $namespace = str_replace('/', '\\', dirname(substr($filename, strlen(ROOT))));
        $tplContents = file_get_contents(DIR_MVC . '/xmvc/core/codeTpl/class.tpl.php');
        $tplContents = qgprintf($tplContents, [
            'site' => $site,
            'namespace' => $namespace,
            'class' => $class,
            'function' => $function,
        ]);
        file_put_contents($filename, $tplContents);
        chmod($filename, 0777);
        MVC::alert('已生成类文件: ' . $filename, '新的类');
        exit();
    }

    /**
     * 创建一个新的 action类, 使用 xmvc/core/codeTpl/action.tpl.php 样板
     *
     * @param [string] $param 目标文件目录,base64编码
     */
    public function newAction(string $newClassName, string $newClassFunction, string $newClassFile)
    {
        if (file_exists($newClassFile)) {
            MVC::warning($newClassFile . ' 控制器文件已存在', '存在');
            exit();
        }
        $site = getSiteNameByDir($newClassFile);
        $site = $site ? $site : '';
        $action = filenameNotExt($newClassFile);
        $namespace = substr($newClassName, 0, strlen($newClassName) - strlen($action) - 1);
        $namespace = str_replace('/', '\\', $namespace);
        $tplContents = file_get_contents(DIR_MVC . '/core/codeTpl/action.tpl.php');
        $tplContents = qgprintf($tplContents, [
            'site' => $site,
            'action' => $action,
            'namespace' => $namespace,
            'function' => $newClassFunction,
        ]);
        mkdirs(dirname($newClassFile));
        file_put_contents($newClassFile, $tplContents);
        chmod($newClassFile, 0777);
        MVC::alert('已生成控制器文件: ' . $newClassFile, '创建');
        exit();
    }

    /**
     * 创建一个新的模板文件, 使用 xmvc/core/codeTpl/tpl.tpl.php 样板
     *
     * @param [string] $param 目标文件目录,base64编码
     */
    public function newTpl(string $newClassFile)
    {
        if (file_exists($newClassFile)) {
            error($newClassFile . ' 模板文件已存在');
        }
        $site = getSiteNameByDir($newClassFile);
        $site = $site ? $site . '/' : '';
        $tplContents = file_get_contents(DIR_MVC . '/core/codeTpl/tpl.tpl.php');
        $tplContents = qgprintf($tplContents, ['site' => $site]);
        mkdirs(dirname($newClassFile));
        file_put_contents($newClassFile, $tplContents);
        chmod($newClassFile, 0777);
        MVC::alert('已生成模板文件: ' . $newClassFile, '新模板');
        exit();
    }

    /**
     * 显示 PHP 文件代码
     *
     * @param string $filename
     * @param int $line 加亮第几行
     */
    public function showPhpCode($filename, $line, $begin = 0, $end = 0, bool $return = false)
    {
        if (!DEBUG) {
            return R()->code(R_NO)->error('DEBUG模式下才有效');
        }
        if ($filename == '') {
            return;
        }
        if (substr($filename, -4) != '.php') {
            $filename .= '.php';
        }
        if ($filename == '' || !is_file($filename)) {
            return;
        }
        $contents = file_get_contents($filename);
        $contents = str_replace("\r\n", "\n", $contents);
        $codes = explode("\n", $contents);
        $count = count($codes);
        $begin = ($begin != 0) ? $line - $begin : 0;
        $end   = ($end != 0) ? $line + $end : $count - 1;
        $begin = ($begin < 0) ? 0 : $begin;
        $end   = ($end > $count - 1) ? $count - 1 : $end;

        if (empty($end)) {
            $end = $count - 1 ;
        }
        $phpStart = false; //找 <?php
        $code_string = '';
        for ($i = $begin * 1; $i <= $end; $i++) {
            $lstr = htmlspecialchars($codes[$i]);
            if (!$phpStart && strpos(trim($lstr), '<?php') !== false) {
                $lstr = str_replace('<?php', '&lt;?php', $lstr);
                $phpStart = true;
            }
            $lstr = strtr($lstr, [
                "\t" => '    ', //制作表转4个空格
                "&#039;" => "'", //还原单引号
            ]);
            $lstr = str_pad((string)($i + 1), 7, ' ', STR_PAD_LEFT) . '    ' . $lstr;
            if ($i == $line - 20) {
                $lstr = "<goto>" . $lstr . "</goto>"; //锚点行
            } elseif ($i == $line - 1) {
                $lstr = "<active>" . $lstr . "</active>"; //高亮行
            }
            if ($code_string != '') {
                $code_string .= "\r\n";
            }
            $code_string .= $lstr;
        }
        $code_string = ltrim($code_string, "\r\n");
        $code_string = Dump::phpCodeColor($code_string);

        $fileInfo = "<div style='background:#007ACC;color:#fff;padding-left:1em'><span style='font-size:.8em;font-weight:bold;color:#FFFF61'>文件:</span> " . relDir($filename) . " 行: " . $line . "</div>";
        $code_string = "\r\n<pre class='phpcode'>\r\n" . $fileInfo . $code_string . "\r\n</pre>\r\n";
        if (!$return) {
            echo "<html><head><title>" . $filename . "</title><style>
            pre.phpcode{
                white-space: pre;
                font-family: Consolas, monaco, Menlo, Courier, monospace;
                font-size:17px;
                line-height:22px;
                background:#fefefe;
                color:#222;
                font-weight:200px;
            }
            </style></head><body>" . $code_string . '</body></html>';
        }
        return R($code_string);
    }

    /**
     * APCu UI
     * @menu
     */
    public function apcu()
    {
        require(DIR_MVC . '/core/cmd/' . __FUNCTION__ . '.php');
        exit();
    }

    /**
     * Redis UI
     * @menu
     */
    public function redis()
    {
        require(DIR_MVC . '/core/cmd/' . __FUNCTION__ . '.php');
        exit;
    }

    /**
     * Beanstalkd 状态信息
     * @menu
     */
    public function beanstalkdStatus()
    {
        $queue = Queue::create('Beanstalk');
        $data = $queue->status();
        dump($data['tubes']);
        dump($data['status']);
        exit;
    }

    /**
     * 创建控制台密码 Hash 值, 默认 admin/Yi&parts.2008 哈希 b2add2944893de6e06a2a9fc7318a3a1
     * @param string $user 用户名
     * @param string $pass 密码
     * @menu
     */
    public function corePass($user, $pass)
    {
        $pass .= '.'; // 未尾加点号防当前面的.后面内容当成扩展名
        MVC::alert((new Power())->passwordForWWWAuth($user, $pass), $user . ' 的控制台密码如下');
        exit();
    }

    public function showErrorLogFile(string $date)
    {
        $filename = DIR_LOG . '/error/' . substr($date, 0, 4) . '/' . $date . '.log';
        $this->showLogFile($filename, 1024);
        exit();
    }

    public function showLogFile(string $filename, $maxline = 0)
    {
        if (is_file($filename)) {
            $contents = file_get_contents($filename);
            if (($fp = fopen($filename, "r")) !== false) {
                $i = 1;
                while (($line = fgets($fp))) {
                    if ($maxline > 0 && $i > $maxline) {
                        break;
                    }
                    preg_match('/\[(.*?)\]\s(.*?)\t\.\r\n/', $line, $m);
                    $date_____________________________________________________ = $m[1];
                    $josn = json_decode($m[2]);
                    dump($date_____________________________________________________);
                    dump($josn);
                    //echo $line;
                    $i++;
                }
                fclose($fp);
            }
        } else {
            MVC::echo('<div class="box-alert"><h1>NOT FOUND ' . relDir($filename) . '</h1><p>日志文件不存在</p></div>');
        }
        exit();
    }

    public function clearErrorLogFile(string $date)
    {
        $filename = DIR_LOG . '/error/' . substr($date, 0, 4) . '/' . $date . '.log';
        if (is_file($filename)) {
            $backupFilename = $filename . '.backup';
            if (!is_file($backupFilename)) {
                copy($filename, $backupFilename);
                unlink($filename);
            } else {
                MVC::echo('<div class="box-alert"><h1>EXISTS ' . relDir($backupFilename) . '</h1><p>已存在备份文件, 如要继续请手动处理.</p></div>');
            }
        } else {
            MVC::echo('<div class="box-alert"><h1>NOT FOUND ' . relDir($filename) . '</h1><p>日志文件不存在</p></div>');
        }
        exit();
    }
}
