<?php
namespace App\Controllers\Admin;

use App\Controllers\Controller;
use Core\App;
use Core\Db;
use Core\Installer;

/**
 * 后台：数据库升级
 *  - 将升级包（*.sql）放入 install/upgrades/ 目录后，本页自动提示「可升级」。
 *  - 支持单个升级与一键全部升级；每次执行写入 db_upgrades 记录（文件名 / 哈希 / 时间 / 操作人）。
 *  - 内容发生变更的已升级包会被重新标记为「需更新」。
 * 路由：/admin/upgrade            -> 升级面板
 *       /admin/upgrade/apply/<file.sql>   -> 升级单个
 *       /admin/upgrade/applyall           -> 一键全部升级
 *       /admin/upgrade/init               -> 基础数据/表结构初始化（POST，仅超管）
 */
class UpgradeController extends Controller
{
    protected $layout = 'layouts/admin';

    private function dir(): string
    {
        return BASE_PATH . '/install/upgrades';
    }

    public function index()
    {
        admin_required();
        if (Db::driver() !== 'mysql') {
            return $this->view('admin/upgrade', ['driver' => 'file']);
        }
        $this->requireSuper();

        Installer::ensureUpgradeLog();
        $files   = $this->scan();
        $applied = $this->appliedMap();

        $list = [];
        foreach ($files as $f) {
            $name = basename($f);
            $hash = md5_file($f);
            $state = !isset($applied[$name]) ? 'pending'
                : ($applied[$name] !== $hash ? 'changed' : 'done');
            $list[] = [
                'name'  => $name,
                'size'  => filesize($f),
                'mtime' => filemtime($f),
                'state' => $state,
            ];
        }
        // 排序：待升级 / 已变更 在前，已应用在后
        $rank = ['pending' => 0, 'changed' => 1, 'done' => 2];
        usort($list, fn($a, $b) => ($rank[$a['state']] ?? 9) - ($rank[$b['state']] ?? 9));

        $pending = count(array_filter($list, fn($x) => $x['state'] !== 'done'));
        $history = Db::query("SELECT * FROM db_upgrades ORDER BY applied_at DESC, id DESC LIMIT 50")->fetchAll();

        return $this->view('admin/upgrade', [
            'driver'  => 'mysql',
            'list'    => $list,
            'pending' => $pending,
            'history' => $history,
        ]);
    }

    /** 升级单个升级包 */
    public function apply($file = null)
    {
        admin_required();
        $this->requireSuper();
        $file = basename((string)$file);
        $path = $this->dir() . '/' . $file;
        if (!is_file($path) || !preg_match('/\.sql$/i', $file)) {
            $this->flash('升级包不存在', 'err');
            $this->redirect('admin/upgrade');
            return '';
        }
        try {
            Installer::applySqlFile($path);
            Installer::ensureUpgradeLog();
            Db::query(
                "INSERT INTO db_upgrades (file, hash, applied_at, applied_by, note) VALUES (?, ?, ?, ?, ?)",
                [$file, md5_file($path), date('Y-m-d H:i:s'), ($_SESSION['admin']['username'] ?? 'admin'), '']
            );
            $this->flash("已升级：{$file}", 'ok');
        } catch (\Throwable $e) {
            $this->flash('升级失败：' . $e->getMessage(), 'err');
        }
        $this->redirect('admin/upgrade');
        return '';
    }

    /** 一键升级全部待处理 */
    public function applyAll()
    {
        admin_required();
        $this->requireSuper();
        $files   = $this->scan();
        $applied = $this->appliedMap();
        $done = 0;
        foreach ($files as $f) {
            $name = basename($f);
            $hash = md5_file($f);
            if (isset($applied[$name]) && $applied[$name] === $hash) continue;
            try {
                Installer::applySqlFile($f);
                Installer::ensureUpgradeLog();
                Db::query(
                    "INSERT INTO db_upgrades (file, hash, applied_at, applied_by, note) VALUES (?, ?, ?, ?, ?)",
                    [$name, $hash, date('Y-m-d H:i:s'), ($_SESSION['admin']['username'] ?? 'admin'), '']
                );
                $done++;
            } catch (\Throwable $e) {
                $this->flash('升级失败：' . e($e->getMessage()), 'err');
                $this->redirect('admin/upgrade');
                return '';
            }
        }
        $this->flash($done > 0 ? "已批量升级 {$done} 个升级包" : '没有需要升级的包', $done > 0 ? 'ok' : 'err');
        $this->redirect('admin/upgrade');
        return '';
    }

    /** 基础数据/表结构初始化（保留旧版能力：补齐新模块表与种子） */
    public function init()
    {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->redirect('admin/upgrade'); return ''; }
        admin_required();
        $this->requireSuper();
        if (!csrf_check()) {
            $this->flash('表单已过期，请刷新后重试', 'err');
            $this->redirect('admin/upgrade');
            return '';
        }
        $msgs = Installer::upgrade();
        $this->flash('基础数据升级完成 ✓ ' . implode('；', $msgs), 'ok');
        $this->redirect('admin/upgrade');
        return '';
    }

    /* ---------------- 工具 ---------------- */

    private function requireSuper(): void
    {
        if (!is_admin() || admin_role() !== 'super_admin') {
            App::forbidden('仅超级管理员可执行数据库升级');
        }
    }

    private function scan(): array
    {
        $d = $this->dir();
        return is_dir($d) ? (glob($d . '/*.sql') ?: []) : [];
    }

    private function appliedMap(): array
    {
        try {
            return Db::query("SELECT file, hash FROM db_upgrades")->fetchAll(\PDO::FETCH_KEY_PAIR);
        } catch (\Throwable $e) {
            return [];
        }
    }
}
