<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\controller;

use Throwable;
use support\Request;
use support\Response;
use plugin\saiadmin\exception\ApiException;
use plugin\saiadmin\basic\OpenController;

/**
 * 安装控制器
 */
class InstallController extends OpenController
{
    /**
     * 不需要登录的方法
     */
    protected array $noNeedLogin = ['index', 'install'];

    /**
     * 应用名称
     * @var string
     */
    protected string $app = 'saiadmin';

    protected string $version = '6.0.0';

    /**
     * 安装首页
     */
    public function index()
    {
        $data['app'] = $this->app;
        $data['version'] = config('plugin.saiadmin.app.version', $this->version);

        $env = base_path() . DIRECTORY_SEPARATOR . '.env';

        clearstatcache();
        if (is_file($env)) {
            $data['error'] = '程序已经安装';
            return view('install/error', $data);
        }

        if (!is_writable(base_path() . DIRECTORY_SEPARATOR . 'config')) {
            $data['error'] = '权限认证失败';
            return view('install/error', $data);
        }

        return view('install/index', $data);
    }

    /**
     * 执行安装
     */
    public function install(Request $request)
    {
        $env = base_path() . DIRECTORY_SEPARATOR . '.env';

        clearstatcache();
        if (is_file($env)) {
            return $this->fail('admin already installed, to reinstall please delete env file and restart');
        }

        $user = $request->post('username');
        $password = $request->post('password');
        $database = $request->post('database');
        $host = $request->post('host');
        $port = (int) $request->post('port') ?: 3306;
        $dataType = $request->post('dataType', 'demo');

        try {
            $db = $this->getPdo($host, $user, $password, $port);
            $smt = $db->query("show databases like '$database'");
            if (empty($smt->fetchAll())) {
                $db->exec("create database `$database` CHARSET utf8mb4 COLLATE utf8mb4_general_ci");
            }
        } catch (\Throwable $e) {
            $message = $e->getMessage();
            if (stripos($message, 'Access denied for user')) {
                return $this->fail('database username or password is incorrect');
            }
            if (stripos($message, 'Connection refused')) {
                return $this->fail('connection refused, please check database ip/port and ensure database is running');
            }
            if (stripos($message, 'timed out')) {
                return $this->fail('database connection timeout, please check ip/port and firewall/security group rules');
            }
            throw $e;
        }

        $db->exec("use `$database`");

        $smt = $db->query("show tables like 'sa_system_menu';");
        $tables = $smt->fetchAll();
        if (count($tables) > 0) {
            return $this->fail('database already installed, please do not install again');
        }

        if ($dataType == 'demo') {
            $sql_file = base_path() . '/plugin/saiadmin/db/saiadmin-6.0.sql';
        } else {
            $sql_file = base_path() . '/plugin/saiadmin/db/saiadmin-pure.sql';
        }

        if (!is_file($sql_file)) {
            return $this->fail('database SQL file not found');
        }

        $sql_query = file_get_contents($sql_file);

        $db->exec($sql_query);

        $this->generateConfig();

        $env_config = <<<EOF
# 数据库配置
DB_TYPE = mysql
DB_HOST = $host
DB_PORT = $port
DB_NAME = $database
DB_USER = $user
DB_PASSWORD = $password
DB_PREFIX = 

# 缓存方式
CACHE_MODE = file

# Redis配置
REDIS_HOST = 127.0.0.1
REDIS_PORT = 6379
REDIS_PASSWORD = ''
REDIS_DB = 0

# 验证码配置
CAPTCHA_MODE = cache

#前端目录
FRONTEND_DIR = saiadmin-artd
EOF;
        file_put_contents(base_path() . DIRECTORY_SEPARATOR . '.env', $env_config);

        // 尝试reload
        if (function_exists('posix_kill')) {
            set_error_handler(function () {});
            posix_kill(posix_getppid(), SIGUSR1);
            restore_error_handler();
        }

        return $this->success('install success');
    }

    /**
     * 生成配置文件 
     */
    protected function generateConfig()
    {
        // 1、think-orm配置文件
        $think_orm_config = <<<EOF
<?php

return [
    'default' => 'mysql',
    'connections' => [
        'mysql' => [
            // 数据库类型
            'type' => env('DB_TYPE', 'mysql'),
            // 服务器地址
            'hostname' => env('DB_HOST', '127.0.0.1'),
            // 数据库名
            'database' => env('DB_NAME', 'saiadmin'),
            // 数据库用户名
            'username' => env('DB_USER', 'root'),
            // 数据库密码
            'password' => env('DB_PASSWORD', '123456'),
            // 数据库连接端口
            'hostport' => env('DB_PORT', 3306),
            // 数据库连接参数
            'params' => [
                // 连接超时3秒
                \PDO::ATTR_TIMEOUT => 3,
            ],
            // 数据库编码默认采用utf8
            'charset' => 'utf8',
            // 数据库表前缀
            'prefix' => env('DB_PREFIX', ''),
            // 断线重连
            'break_reconnect' => true,
            // 自定义分页类
            'bootstrap' =>  '',
            // 连接池配置
            'pool' => [
                'max_connections' => 5, // 最大连接数
                'min_connections' => 1, // 最小连接数
                'wait_timeout' => 3,    // 从连接池获取连接等待超时时间
                'idle_timeout' => 60,   // 连接最大空闲时间，超过该时间会被回收
                'heartbeat_interval' => 50, // 心跳检测间隔，需要小于60秒
            ],
        ],
    ],
];
EOF;
        file_put_contents(base_path() . '/config/think-orm.php', $think_orm_config);

        // 2、chache配置文件
        $cache_config = <<<EOF
<?php

return [
    'default' => env('CACHE_MODE', 'file'),
    'stores' => [
        'file' => [
            'driver' => 'file',
            'path' => runtime_path('cache')
        ],
        'redis' => [
            'driver' => 'redis',
            'connection' => 'default'
        ],
        'array' => [
            'driver' => 'array'
        ]
    ]
];
EOF;
        file_put_contents(base_path() . '/config/cache.php', $cache_config);

        // 3、redis配置文件
        $redis_config = <<<EOF
<?php

return [
    'default' => [
        'password' => env('REDIS_PASSWORD', ''),
        'host' => env('REDIS_HOST', '127.0.0.1'),
        'port' => env('REDIS_PORT', 6379),
        'database' => env('REDIS_DB', 0),
        'pool' => [
            'max_connections' => 5,
            'min_connections' => 1,
            'wait_timeout' => 3,
            'idle_timeout' => 60,
            'heartbeat_interval' => 50,
        ],
    ]
];
EOF;
        file_put_contents(base_path() . '/config/redis.php', $redis_config);

        // 4、think-cache配置文件
        $think_cache_config = <<<EOF
<?php
return [
    // 默认缓存驱动
    'default' => env('CACHE_MODE', 'file'),
    // 缓存连接方式配置
    'stores'  => [
        // redis缓存
        'redis' => [
            // 驱动方式
            'type' => 'redis',
            // 服务器地址
            'host' => env('REDIS_HOST', '127.0.0.1'),
            // 服务器端口
            'port' => env('REDIS_PORT', 6379),
            // 服务器密码
            'password' => env('REDIS_PASSWORD', ''),
            // 数据库
            'select' => env('REDIS_DB', 0),
            // 缓存前缀
            'prefix' => 'cache:',
            // 默认缓存有效期 0表示永久缓存
            'expire'     => 0,
            // Thinkphp官方没有这个参数，由于生成的tag键默认不过期，如果tag键数量很大，避免长时间占用内存，可以设置一个超过其他缓存的过期时间，0为不设置
            'tag_expire' => 86400 * 30,
            // 缓存标签前缀
            'tag_prefix' => 'tag:',
            // 连接池配置
            'pool' => [
                'max_connections' => 5, // 最大连接数
                'min_connections' => 1, // 最小连接数
                'wait_timeout' => 3,    // 从连接池获取连接等待超时时间
                'idle_timeout' => 60,   // 连接最大空闲时间，超过该时间会被回收
                'heartbeat_interval' => 50, // 心跳检测间隔，需要小于60秒
            ],
        ],
        // 文件缓存
        'file' => [
            // 驱动方式
            'type' => 'file',
            // 设置不同的缓存保存目录
            'path' => runtime_path() . '/file/',
        ],
    ],
];
EOF;
        file_put_contents(base_path() . '/config/think-cache.php', $think_cache_config);

        // 5、database配置文件
        $database = <<<EOF
<?php
return [
    'default' => 'mysql',
    'connections' => [
        'mysql' => [
            'driver' => env('DB_TYPE', 'mysql'),
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', 3306),
            'database' => env('DB_NAME', 'saiadmin'),
            'username' => env('DB_USER', 'root'),
            'password' => env('DB_PASSWORD', '123456'),
            'charset' => env('DB_CHARSET', 'utf8mb4'),
            'collation' => env('DB_COLLATION', 'utf8mb4_general_ci'),
            'prefix' => env('DB_PREFIX', ''),
            'strict' => true,
            'engine' => null,
            'options' => [
                PDO::ATTR_EMULATE_PREPARES => false, // Must be false for Swoole and Swow drivers.
            ],
            'pool' => [
                'max_connections' => 5,
                'min_connections' => 1,
                'wait_timeout' => 3,
                'idle_timeout' => 60,
                'heartbeat_interval' => 50,
            ],
        ],
    ],
];
EOF;
        file_put_contents(base_path() . '/config/database.php', $database);

    }

    /**
     * 获取pdo连接
     * @param $host
     * @param $username
     * @param $password
     * @param $port
     * @param $database
     * @return \PDO
     */
    protected function getPdo($host, $username, $password, $port, $database = null): \PDO
    {
        $dsn = "mysql:host=$host;port=$port;";
        if ($database) {
            $dsn .= "dbname=$database";
        }
        $params = [
            \PDO::MYSQL_ATTR_INIT_COMMAND => "set names utf8mb4",
            \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
            \PDO::ATTR_EMULATE_PREPARES => false,
            \PDO::ATTR_TIMEOUT => 5,
            \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
        ];
        return new \PDO($dsn, $username, $password, $params);
    }
}
