<?php

namespace APP\api\admin;

use APP\DI;
use APP\program\admin\Cache;
use APP\program\admin\Cmder;
use APP\program\admin\Component;
use APP\program\admin\Plugin;
use APP\program\admin\Setting;
use APP\program\admin\Verify;
use APP\program\Resource;
use Exception;
use LyApi\core\classify\API as ClassifyAPI;
use LyApi\core\error\CustomException;
use LyApi\core\request\Request;
use LyApi\LyApi;
use LyApi\tools\Config;
use LyApi\tools\Launch;
use Medoo\Medoo;
use PDOException;
use Plugin\LyMaster\LyMaster;

class api extends ClassifyAPI
{

    private $develop_setting;

    public function __construct()
    {
        $this->develop_setting = Config::getConfig('develop', 'admin', 'json');
    }

    // 系统操作程序
    public function system($type, $args)
    {
        if ($type == 'API') {
            $service = Request::Get('service');
        } else {
            $service = $args['service'];
        }

        if ($service != '') {
            if ($service == 'info') {

                $master = new LyMaster();

                return [
                    'php-version' => PHP_VERSION,
                    'system-name' => PHP_OS,
                    'lyapi-version' => LyApi::$version,
                    'lymaster-version' => $master->GetPluginVersion()
                ];
            } elseif ($service == 'check') {

                $error_list = [];

                // 检查文件夹权限
                if (!is_writable(LyApi . '/app/api/admin.php')) {
                    // 接口程序文件不可写入
                    array_push($error_list, ['文件权限', '接口不可写']);
                }

                if (!is_writable(LyApi . '/data/database/install.sql')) {
                    // 数据储存文件不可写入
                    array_push($error_list, ['文件权限', '数据不可写']);
                }

                if (!is_writable(LyApi . '/config/admin/develop.json')) {
                    // 配置系统文件不可写入
                    array_push($error_list, ['文件权限', '配置不可写']);
                }

                return $error_list;
            } else if ($service == 'cexec') {
                // 检查系统是否支持 exec 函数
                if (function_exists('exec')) {
                    return 'yes';
                } else {
                    return 'no';
                }
            } else if ($service == 'mtool.data') {
                if (class_exists("Admin\plugin\BTool\options")) {
                    $open_api = '/btool/api/';
                } else {
                    $open_api = false;
                }

                return [
                    "open_api" => $open_api
                ];
            } else if ($service == 'change.index') {
                // 更换主页面权限 FOR 插件
                $plugin = Request::Get('plugin');
                if ($plugin == '') {
                    return [
                        '#code' => 400,
                        '#msg' => '参数验证错误'
                    ];
                }

                if (Verify::isLogin() && $_SESSION['userid'] == '1') {
                    $db_connect = Setting::dbConnect();
                    if ($db_connect->has('plugin', ['name' => $plugin])) {
                        $cute = Setting::localDB('plugin');
                        $cute->set('index_control', $db_connect->get('plugin', 'sign', [
                            'name' => $plugin
                        ]), true);
                        return "OK";
                    } else {
                        return ['#code' => '400', '#msg' => '未知的请求插件'];
                    }
                } else {
                    return ['#code' => '401', '#msg' => '账号无权限'];
                }
            } else {
                return [
                    '#code' => 400,
                    '#msg' => '未知的请求目标'
                ];
            }
        } else {
            return [
                '#code' => 400,
                '#msg' => '参数验证错误'
            ];
        }
    }

    // 程序安装接口
    public function install($type, $args)
    {
        if (!Setting::isInstall()) {
            $db_setting = [];

            $db_setting['host'] = Request::Post('host');
            $db_setting['port'] = Request::Post('port');
            $db_setting['name'] = Request::Post('name');
            $db_setting['user'] = Request::Post('user');
            $db_setting['pass'] = Request::Post('pass');
            $addition_function  = Request::Post('down');

            // 测试数据库链接
            try {
                $test_connect = new Medoo([
                    'database_type' => 'mysql',
                    'database_name' => $db_setting['name'],
                    'server' => $db_setting['host'],
                    'username' => $db_setting['user'],
                    'password' => $db_setting['pass'],

                    // [optional]
                    'charset' => 'utf8',
                    'port' => $db_setting['port'],
                    'prefix' => 'admin_',
                ]);
            } catch (PDOException $e) {
                return ['#msg' => '测试连接失败'];
            }

            // 安装开始前检测是否有写入权限
            if (!is_writable(LyApi . '/config/admin/coder.json')) {
                return ['#code' => '500', '#msg' => '没有文件写入权限'];
            }

            Config::setConfig('database', [
                'database_type' => 'mysql',
                'database_name' => $db_setting['name'],
                'server' => $db_setting['host'],
                'username' => $db_setting['user'],
                'password' => $db_setting['pass'],

                // [optional]
                'charset' => 'utf8',
                'port' => $db_setting['port'],
                'prefix' => 'admin_',
            ], 'cover', 'admin');

            $admin_secret = base64_encode(rand());
            $admin_passwd = rand(100000, 999999);
            $admin_passwd_code = Verify::encryptPwd($admin_passwd, $admin_secret);

            $sql_file = file_get_contents(LyApi . '/data/database/install.sql');

            $sql_file = str_replace('$prefix$', 'admin_', $sql_file);
            $sql_file = str_replace('$secret$', $admin_secret, $sql_file);
            $sql_file = str_replace('$password$', $admin_passwd_code, $sql_file);
            $sql_file = str_replace('$domain$', $_SERVER['SERVER_NAME'], $sql_file);
            $sql_file = str_replace('$time$', time(), $sql_file);

            try {
                $status = $test_connect->query($sql_file);
            } catch (Exception $e) {
                return ['#code' => 400, '#msg' => '安装出现错误'];
            }

            if ($status->errorCode() == '00000') {
                return [
                    'username' => 'admin',
                    'password' => $admin_passwd
                ];
            } else {
                return [
                    '#code' => 400,
                    '#msg' => '安装出现错误'
                ];
            }
        } else {

            // 目录、文件检测
            if (!is_dir(LyApi . '/data/database/user')) {
                mkdir(LyApi . '/data/database/user');
            }

            if (!is_dir(LyApi . '/data/database/local')) {
                mkdir(LyApi . '/data/database/local');
            }

            // 初始化插件信息
            $plugin_manager = new Plugin();
            $plugin_manager->reloadPlugin();

            return [
                '#msg' => '程序已安装'
            ];
        }
    }

    // 管理员登录接口
    public function login($type, $args)
    {
        if ($type == 'API') {
            $username = Request::Post('username');
            $password = Request::Post('password');
        } else {
            $username = $args['username'];
            $password = $args['password'];
        }

        if ($username != '' && $password != '') {

            // 检测是否限制登录
            $cache = DI::FileCache($this->develop_setting['cache-setting']['file']['rpath'] . '/login');
            if ($cache->has('loginLimit')) {
                $limits = $cache->get('loginLimit');
                if (array_key_exists($_SERVER["REMOTE_ADDR"], $limits)) {
                    if ($limits[$_SERVER["REMOTE_ADDR"]] >= 5) {
                        return ['#msg' => '安全系统限制'];
                    }
                }
            }

            // 登录检测
            $db_connect = Setting::dbConnect();
            if ($db_connect->has('users', ['username' => $username])) {
                $udata = $db_connect->get('users', ['id', 'nickname', 'secret'], ['username' => $username]);
                $enpwd = Verify::encryptPwd($password, $udata['secret']);
                if ($db_connect->has('users', ['username' => $username, 'password' => $enpwd])) {
                    if ($db_connect->has('users', ['username' => $username, 'usable' => 1])) {
                        $user_token = rand(10000, 99999);

                        $_SESSION['username'] = $username;
                        $_SESSION['userid'] = $udata['id'];
                        $_SESSION['nickname'] = $udata['nickname'];

                        return [
                            'status' => 'OK',
                            'userid' => $udata['id']
                        ];
                    } else {
                        return ['#code' => '401', '#msg' => '账号已封禁'];
                    }
                }
            }

            if ($cache->has('loginLimit')) {
                $new_data = $cache->get('loginLimit');
                if (!array_key_exists($_SERVER["REMOTE_ADDR"], $new_data)) {
                    $new_data[$_SERVER["REMOTE_ADDR"]] = 1;
                } else {
                    $new_data[$_SERVER["REMOTE_ADDR"]]++;
                }
                $cache->set('loginLimit', $new_data, 5 * 60);
            } else {
                $cache->set('loginLimit', [
                    $_SERVER["REMOTE_ADDR"] => 1
                ], 5 * 60);
            }
            return ['#msg' => '用户数据错误'];
        } else {
            return ['#code' => 400, '#msg' => '参数不完整'];
        }
    }

    // 系统配置操作接口
    public function settings($type, $args)
    {
        if (Verify::isLogin()) {
            $do = $args['do'];
            if ($do == 'update') {

                if (array_key_exists('which', $args)) {
                    $which = $args['which'];
                } else {
                    $which = 'system';
                }

                if ($which == 'system') {
                    if (Verify::authCheck('dosetting')) {

                        $datas = [];

                        $datas['site_title'] = $args['title'];
                        $datas['site_notice'] = $args['notice'];
                        $datas['site_cache_time'] = $args['cache'];
                        $datas['sit_copyright'] = $args['copyroght'];

                        $db_connect = Setting::dbConnect();

                        foreach ($datas as $key => $value) {
                            $db_connect->update('setting', [
                                'context' => $value
                            ], ['comment' => $key]);
                        }

                        return 'OK';
                    } else {
                        return ['#code' => '401', '#msg' => '账号无权限'];
                    }
                } else if ($which == 'develop') {
                    if (Verify::authCheck('dosetting')) {

                        if (array_key_exists('context', $args) && array_key_exists('password', $args)) {
                            $db_connect = Setting::dbConnect();
                            $secret = $db_connect->get('users', 'secret', ['id' => $_SESSION['userid']]);

                            if ($args['context'] != '') {
                                if ($db_connect->has('users', [
                                    'id' => $_SESSION['userid'],
                                    'password' => Verify::encryptPwd($args['password'], $secret)
                                ])) {
                                    file_put_contents(LyApi . '/config/admin/develop.json', $args['context']);
                                    return 'OK';
                                }
                                return ['#code' => '401', '#msg' => '密码验证错误'];
                            } else {
                                return ['#code' => '400', '#msg' => '不能保存空内容'];
                            }
                        } else {
                            return ['#code' => '400', '#msg' => '参数不完整'];
                        }

                        return 'OK';
                    } else {
                        return ['#code' => '401', '#msg' => '账号无权限'];
                    }
                }
                return ['#code' => '400', '#msg' => '未知操作目标'];
            }
        }
        return ['#code' => '401', '#msg' => '账号未登录'];
    }

    // coder 在线编辑器程序接口
    public function coder($type, $args)
    {
        $do = $args['do'];

        $config = Config::getConfig('coder', 'admin', 'json');

        // 创建新的 coder 打开程序
        if ($do == 'save') {

            // 预留用户权限判断点
            if (false) {
            }

            // 保存代码内容
            $type = $args['type'];
            $file = $args['file'];
            $context = $args['context'];

            // 筛出特别情况
            if ($type == '' && $file == 'setting.json') {
                $file_path = LyApi . '/config/admin/coder.json';
                if (file_exists($file_path)) {
                    file_put_contents($file_path, $context);
                    return 'OK';
                } else {
                    return ['#code' => '400', '#msg' => '文件数据不存在'];
                }
            } else if ($type == 'maker') {
                $cache = DI::FileCache('admin/maker');
                $funcs = $cache->get("func-list");
                $file = str_replace("MAKERC.", "", $file);

                $comp = new Component();

                $template = "\tpublic function " . $comp->template("get", $file)['func'] . "(\$type,\$args){";
                $template .= $context;
                $template .= "}";


                $funcs[$file] = $template;
                $cache->set("func-list", $funcs);
                return 'OK';
            }

            $file = str_replace('\\', '/', $file);

            $file_path = LyApi . '/app/' . $type . '/' . $file;
            if (file_exists($file_path)) {
                file_put_contents($file_path, $context);
                return 'OK';
            }

            return ['#code' => '400', '#msg' => '文件数据不存在'];
        } elseif ($do == 'open') {
            $file = $args['file'];

            if ($file == 'setting.json') {
                return Config::getConfig('coder', 'admin', 'json');
            } else {
                $type = $args['type'];
                if ($type == 'api' || $type == 'view' || $type == 'program') {

                    $file = str_replace('\\', '/', $file);

                    if (file_exists(LyApi . '/app/' . $type . '/' . $file)) {
                        return file_get_contents(LyApi . '/app/' . $type . '/' . $file);
                    }
                } else if ($type == 'maker') {
                    $file = str_replace("MAKERC.", "", $file);

                    $cache = DI::FileCache('admin/maker');
                    $funcs = $cache->get('func-list');

                    if (array_key_exists($file, $funcs)) {
                        $comp = new Component();
                        return [
                            "code" => $funcs[$file],
                            "info" => $comp->template("get", $file)
                        ];
                    }
                }
            }

            return '目标文件未找到...';
        } elseif ($do == 'setting') {
            return Config::getConfig('coder', 'admin', 'json')[$args['which']];
        } elseif ($do == 'overview') {
            // 系统总览程序
            if (Verify::isLogin()) {
                if (Verify::authCheck('docoder')) {

                    if (!array_key_exists('type', $args)) {
                        $args['type'] = 'read';
                    }

                    if ($args['type'] == 'read') {
                        if (array_key_exists('file', $args) && array_key_exists('path', $args)) {

                            // 系统安全验证：访问文件是否超出 系统限制

                            // $root_path = Config::getConfig('summit', 'admin', 'json')['system']['root_path'];
                            $root_path = LyApi;

                            $root_path = explode('/', $root_path);
                            $from_path = explode('/', $args['path']);

                            foreach ($root_path as $key => $value) {
                                if ($root_path[$key] != $from_path[$key]) {
                                    return '无法访问系统外的文件信息...';
                                }
                            }

                            if (strpos($args['path'], '..')) {
                                return '路径包含危险字符串...';
                            }

                            // $from_path = substr($args['path'], 0, strlen($root_path));

                            // if ($root_path != $from_path) {
                            //     return '无法访问系统外的文件信息...';
                            // }



                            if (file_exists($args['path'] . '/' . $args['file'])) {
                                return file_get_contents($args['path'] . '/' . $args['file']);
                            } else {
                                return '文件不存在';
                            }
                        } else {
                            return '';
                        }
                    } else if ($args['type'] == 'write') {
                        if (array_key_exists('file', $args) && array_key_exists('path', $args) && array_key_exists('context', $args)) {

                            // 系统安全验证：访问文件是否超出 系统限制

                            // $root_path = Config::getConfig('summit', 'admin', 'json')['system']['root_path'];
                            $root_path = LyApi;

                            $root_path = explode('/', $root_path);
                            $from_path = explode('/', $args['path']);

                            foreach ($root_path as $key => $value) {
                                if ($root_path[$key] != $from_path[$key]) {
                                    return '无法访问系统外的文件信息...';
                                }
                            }

                            if (strpos($args['path'], '..')) {
                                return '路径包含危险字符串...';
                            }

                            if (file_exists($args['path'] . '/' . $args['file'])) {
                                if (is_writeable($args['path'] . '/' . $args['file'])) {

                                    // 更新文件前对内容进行备份
                                    // $backup_data = file_get_contents($args['path'] . '/' . $args['file']);
                                    // $last_dir = explode('/',$args['path']);
                                    // $last_dir = array_pop($last_file);
                                    // $backup_path = $root_path . '/admin/backup/files/' . $last_dir . '-' . $args['file'];
                                    // file_put_contents($backup_path,$backup_data);

                                    file_put_contents($args['path'] . '/' . $args['file'], $args['context']);
                                    return 'OK';
                                } else {
                                    return ['#code' => '401', '#msg' => '文件不可写'];
                                }

                                // return file_get_contents($args['path'] . '/' . $args['file']);
                            } else {
                                return ['#code' => '404', '#msg' => '文件不存在'];
                            }
                        } else {
                            return ['#code' => '400', '#msg' => '参数不完整'];
                        }
                    } else {
                        return ['#code' => '400', '#msg' => '未知的操作方法'];
                    }
                }
            }
            return ['#code' => '401', '#msg' => '当前账号无操作权限...'];
        }
        return ['#code' => '400', '#msg' => '无法定位目标'];
    }

    public function cmder()
    {
        $commander = new Cmder();
        return $commander->explain(Request::Request('code'));
    }

    // 专门为 Layui 表格程序提供的格式转换接口
    public function layui_table($type, $args)
    {

        // Layui 识别的接口格式
        $result = [
            'code' => 0,
            'msg' => '',
            'count' => 0,
            'data' => []
        ];

        if ($type == 'API') {
            $path = Request::Request('path');
            $func = Request::Request('func');
            $parg = $_REQUEST;
            unset($parg['path']);
            unset($parg['func']);
        } else {
            $path = $args['path'];
            $func = $args['func'];
            $parg = $args['args'];
        }

        if ($path != '' && $func != '') {

            $path = str_replace('-', '\\', $path);

            $api_data = Launch::LaunchApi($path, $func, $parg);
            if ($api_data != null) {
                // 接口程序存在则生成对应格式接口

                if ($api_data['code'] == "200") {
                    if (gettype($api_data['data']) == 'array') {
                        if ($api_data['data'] != []) {
                            if (Request::Request('count') == '') {
                                $result['data'] = $api_data['data'];
                                $result['count'] = count($api_data['data']);
                            } else {
                                $result['data'] = $api_data['data'];
                                $result['count'] = Request::Request('count');
                            }
                        } else {
                            $result['code'] = '3';
                            $result['msg'] = $api_data['message'];
                        }
                    } elseif (gettype($api_data['data'] == 'string')) {
                        $result['code'] = '4';
                        $result['msg'] = $api_data['data'];
                    } else {
                        $result['code'] = '4';
                        $result['msg'] = $api_data['message'];
                    }
                } else {
                    $result['code'] = '2';
                    $result['msg'] = $api_data['message'];
                }
                throw new CustomException(json_encode($result, JSON_UNESCAPED_UNICODE), 200);
            }
        }
        $result['code'] = '1';
        $result['msg'] = '无法定位目标接口...';
        throw new CustomException(json_encode($result, JSON_UNESCAPED_UNICODE), 200);
    }

    public function acode($type, $args)
    {

        if (array_key_exists('from', $args)) {

            // 检查 验证码状况
            if (array_key_exists('check', $args)) {
                $cache = DI::FileCache($this->develop_setting['cache-setting']['file']['rpath'] . '/acode');
                if ($cache->has($args['from'])) {
                    if ($cache->get($args['from']) == $args['check']) {
                        $cache->delete($args['from']);
                        return 'OK';
                    } else {
                        return ['#msg' => '验证码输入错误'];
                    }
                } else {
                    return ['#msg' => '验证码数据不存在'];
                }
            }

            $string = '';
            $arr = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
            for ($i = 0; $i < 4; $i++) {
                $string .= $arr[rand(0, count($arr) - 1)];
            }

            // 数据缓存
            $cache = DI::FileCache($this->develop_setting['cache-setting']['file']['rpath'] . '/acode');
            $cache->set($args['from'], $string, 60);

            header('Content-type:image/jpeg');
            $width = 120;
            $height = 40;
            $img = imagecreatetruecolor($width, $height); //imagecreatetruecolor函数建一个真彩色图像

            //生成彩色像素    
            $colorBg = imagecolorallocate($img, rand(200, 255), rand(200, 255), rand(200, 255)); //背景     imagecolorallocate函数为一幅图像分配颜色

            imagefill($img, 0, 0, $colorBg);

            //该循环,循环画背景干扰的点
            for ($m = 0; $m <= 100; $m++) {
                $pointcolor = imagecolorallocate($img, rand(0, 255), rand(0, 255), rand(0, 255)); //点的颜色
                imagesetpixel($img, rand(0, $width - 1), rand(0, $height - 1), $pointcolor); // 水平地画一串像素点
            }

            //该循环,循环画干扰直线
            for ($i = 0; $i <= 4; $i++) {
                $linecolor = imagecolorallocate($img, rand(0, 255), rand(0, 255), rand(0, 255)); //线的颜色
                imageline($img, rand(0, $width), rand(0, $height), rand(0, $width), rand(0, $height), $linecolor); //画一条线段
            }

            $colorString = imagecolorallocate($img, rand(10, 100), rand(10, 100), rand(10, 100)); //文本

            //2种插入字符串字体的方式
            imagefttext($img, 18, 0, rand(5, 15), rand(30, 35), $colorString, LyApi . '/app/view/static/admin/font/okomito.ttf', $string);
            // imagestring($img, 5, rand(0, $width - 36), rand(0, $height - 15), $string, $colorString);

            //输出图片到浏览器
            imagejpeg($img);

            //销毁，释放资源
            imagedestroy($img);
            throw new CustomException('', 200);
        } else {
            return ['#code' => '400', '#msg' => '未知接口来源'];
        }
    }

    // 清空缓存操作
    public function clear($type, $args)
    {
        Cache::cleanFileCahce();
        throw new CustomException(json_encode(['code' => '1', 'msg' => '后台缓存清理成功...'], JSON_UNESCAPED_UNICODE), 200);
    }

    // 渲染 Doc 目录程序
    public function doccp($type, $args)
    {
        if (array_key_exists('which', $args)) {
            $which = $args['which'];
            if ($which == 'notice') {
                $context = file_get_contents(LyApi . '/data/doc/notice.html');
                throw new CustomException($context, 200);
            }
        }
        return ['#code' => '400', '#msg' => '未知数据来源'];
    }

    // 图表渲染操作
    public function charts($type, $args)
    {
        if (array_key_exists('which', $args)) {
            if ($args['which'] == 'vstate-all') {
                $cache = Cache::cacheObject('vstate', true);
                $ctype = $cache[1];
                $cache = $cache[0];

                if ($ctype == 'file') {
                    $datas = $cache->get('all');
                } else {
                    $datas = json_decode($cache->hget('admin_vstate', 'all'), true);
                }

                $dates = [];
                $nums = [];

                // foreach ($datas as $key => $value) {
                //     array_push($dates,$key);
                //     array_push($nums,$value);
                // }

                for ($i = 7; $i >= 0; $i--) {
                    $date = date('Y-m-d', strtotime('-' . $i . ' days'));
                    array_push($dates, $date);
                    if (array_key_exists($date, $datas)) {
                        array_push($nums, $datas[$date]);
                    } else {
                        array_push($nums, 0);
                    }
                }


                return [
                    'dates' => $dates,
                    'nums' => $nums
                ];
            }
        }
    }
}
