<?php
namespace App\Controllers\Admin;

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

/**
 * 后台：数据库管理
 *  - 列出全部数据表（MySQL 模式）。
 *  - 浏览任意表数据、查看/编辑/新增/删除记录（基于主键）。
 *  - 仅 MySQL 模式可用；file 模式仅做说明提示。
 *
 * 路由说明：App::dispatchAdmin 会以 URL 段作为方法名调用本控制器，
 * 故 browse/edit/update/create/store/delete 均为 public，并在内部用
 * App::parseRoute() 重新解析完整路径（含第 3、4 段）以拿到表名与记录主键。
 *   /admin/db                     -> index()        表列表
 *   /admin/db/browse/<table>      -> browse(table)  浏览（?q= 搜索、?page= 分页）
 *   /admin/db/edit/<table>/<id>   -> edit(table,id) 编辑表单
 *   /admin/db/update/<table>/<id> -> update(...)    保存（POST）
 *   /admin/db/create/<table>      -> create(table)  新增表单
 *   /admin/db/store/<table>       -> store(...)     新增保存（POST）
 *   /admin/db/delete/<table>/<id> -> delete(...)    删除（GET + 确认）
 *
 * 安全：表名与列名均来自 DESCRIBE / SHOW TABLES 白名单，杜绝 SQL 注入。
 */
class DatabaseController extends Controller
{
    protected $layout = 'layouts/admin';

    /** 解析完整路径：['admin','db', action, table, id] */
    private function route(): array
    {
        return \Core\App::parseRoute();
    }

    /** 路由中的表名段（full[3]） */
    private function segTable(): ?string
    {
        $r = $this->route();
        return $r[3] ?? null;
    }

    /** 路由中的记录主键段（full[4]） */
    private function segId(): ?string
    {
        $r = $this->route();
        return $r[4] ?? null;
    }

    /** 表列表 */
    public function index($ignored = null)
    {
        admin_required();
        if (Db::driver() !== 'mysql') {
            return $this->view('admin/db_tables', [
                'fileMode' => true,
                'dataFiles' => $this->fileDataFiles(),
            ]);
        }
        return $this->tablesList();
    }

    /** 浏览记录 */
    public function browse($table = null)
    {
        admin_required();
        if (Db::driver() !== 'mysql') { return $this->tablesList(); }
        $table = $table ?? $this->segTable();
        return $this->doBrowse($table);
    }

    /** 编辑 / 新增表单 */
    public function edit($table = null)
    {
        admin_required();
        if (Db::driver() !== 'mysql') { return $this->tablesList(); }
        $table = $table ?? $this->segTable();
        $id = $this->segId();
        return $this->doEditForm($table, $id);
    }

    /** 保存编辑 */
    public function update($table = null)
    {
        admin_required();
        $table = $table ?? $this->segTable();
        $id = $this->segId();
        return $this->doUpdate($table, $id);
    }

    /** 新增表单 */
    public function create($table = null)
    {
        admin_required();
        if (Db::driver() !== 'mysql') { return $this->tablesList(); }
        $table = $table ?? $this->segTable();
        return $this->doEditForm($table, null);
    }

    /** 保存新增 */
    public function store($table = null)
    {
        admin_required();
        $table = $table ?? $this->segTable();
        return $this->doStore($table);
    }

    /** 删除 */
    public function delete($table = null)
    {
        admin_required();
        $table = $table ?? $this->segTable();
        $id = $this->segId();
        return $this->doDelete($table, $id);
    }

    /* ---------------- 实现 ---------------- */

    private function tablesList(): string
    {
        $pdo = Db::pdo();
        $status = $pdo->query("SHOW TABLE STATUS")->fetchAll();
        $tables = [];
        foreach ($status as $t) {
            $tables[] = [
                'name'   => $t['Name'],
                'engine' => $t['Engine'] ?? '',
                'rows'   => (int)($t['Rows'] ?? 0),
                'size'   => (int)($t['Data_length'] ?? 0) + (int)($t['Index_length'] ?? 0),
                'collation' => $t['Collation'] ?? '',
            ];
        }
        usort($tables, fn($a, $b) => strcmp($a['name'], $b['name']));
        return $this->view('admin/db_tables', ['tables' => $tables, 'total' => count($tables)]);
    }

    private function doBrowse(?string $table): string
    {
        if (!$table || !$this->validTable($table)) {
            $this->flash('表不存在或无权访问', 'err');
            return $this->tablesList();
        }
        $pdo    = Db::pdo();
        $cols   = $this->columnsOf($table);
        $names  = array_column($cols, 'Field');
        $pk     = $this->pkOf($table) ?: $names[0];

        $limit  = 50;
        $page   = max(1, (int)($_GET['page'] ?? 1));
        $offset = ($page - 1) * $limit;
        $q      = trim((string)($_GET['q'] ?? ''));

        $where  = '';
        $params = [];
        if ($q !== '') {
            $likes = [];
            foreach ($names as $c) {
                $likes[] = "`{$c}` LIKE ?";
                $params[] = '%' . $q . '%';
            }
            $where = ' WHERE ' . implode(' OR ', $likes);
        }

        $stmt = $pdo->prepare("SELECT COUNT(*) FROM `{$table}`" . $where);
        $stmt->execute($params);
        $total = (int)$stmt->fetchColumn();

        $orderCol = in_array($pk, $names, true) ? $pk : $names[0];
        $rows = $pdo->query("SELECT * FROM `{$table}`" . $where . " ORDER BY `{$orderCol}` DESC LIMIT {$limit} OFFSET {$offset}")->fetchAll(\PDO::FETCH_ASSOC);

        $totalPages = max(1, (int)ceil($total / $limit));
        return $this->view('admin/db_browse', [
            'table'   => $table,
            'cols'    => $cols,
            'names'   => $names,
            'pk'      => $pk,
            'rows'    => $rows,
            'page'    => $page,
            'totalPages' => $totalPages,
            'total'   => $total,
            'q'       => $q,
        ]);
    }

    private function doEditForm(?string $table, $id): string
    {
        if (!$table || !$this->validTable($table)) {
            $this->flash('表不存在或无权访问', 'err');
            return $this->tablesList();
        }
        $pdo = Db::pdo();
        $cols = $this->columnsOf($table);
        $pk = $this->pkOf($table);
        $row = null;
        if ($id !== null && $id !== '') {
            $stmt = $pdo->prepare("SELECT * FROM `{$table}` WHERE `{$pk}` = ? LIMIT 1");
            $stmt->execute([$id]);
            $row = $stmt->fetch(\PDO::FETCH_ASSOC);
            if (!$row) {
                $this->flash('记录不存在', 'err');
                return $this->doBrowse($table);
            }
        }
        return $this->view('admin/db_form', [
            'table' => $table,
            'cols'  => $cols,
            'pk'    => $pk,
            'row'   => $row,
            'mode'  => $row ? 'edit' : 'create',
        ]);
    }

    private function doUpdate(?string $table, $id): string
    {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->redirect("admin/db/browse/$table"); return ''; }
        if (!$table || !$this->validTable($table)) { $this->flash('表不存在', 'err'); return $this->tablesList(); }
        $pdo = Db::pdo();
        $cols = $this->columnsOf($table);
        $pk = $this->pkOf($table);
        $sets = [];
        $params = [];
        foreach ($cols as $c) {
            $f = $c['Field'];
            if ($f === $pk) continue;
            if (!array_key_exists($f, $_POST)) continue;
            $v = $_POST[$f];
            if ($v === '' && $c['Null'] === 'YES') $v = null;
            $sets[] = "`{$f}` = ?";
            $params[] = $v;
        }
        if (empty($sets)) {
            $this->flash('没有需要更新的字段', 'ok');
            $this->redirect("admin/db/browse/$table");
            return '';
        }
        $params[] = $id;
        $pdo->prepare("UPDATE `{$table}` SET " . implode(', ', $sets) . " WHERE `{$pk}` = ?")->execute($params);
        $this->flash('记录已更新', 'ok');
        $this->redirect("admin/db/browse/$table");
        return '';
    }

    private function doStore(?string $table): string
    {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->redirect("admin/db/browse/$table"); return ''; }
        if (!$table || !$this->validTable($table)) { $this->flash('表不存在', 'err'); return $this->tablesList(); }
        $pdo = Db::pdo();
        $cols = $this->columnsOf($table);
        $fields = [];
        $ph = [];
        $params = [];
        foreach ($cols as $c) {
            $f = $c['Field'];
            if (($c['Extra'] ?? '') === 'auto_increment') continue;
            if (!array_key_exists($f, $_POST)) {
                if ($c['Null'] === 'YES') { $fields[] = "`{$f}`"; $ph[] = '?'; $params[] = null; }
                continue;
            }
            $v = $_POST[$f];
            if ($v === '' && $c['Null'] === 'YES') $v = null;
            $fields[] = "`{$f}`"; $ph[] = '?'; $params[] = $v;
        }
        if (empty($fields)) {
            $this->flash('没有可写入的字段', 'err');
            $this->redirect("admin/db/browse/$table");
            return '';
        }
        $pdo->prepare("INSERT INTO `{$table}` (" . implode(', ', $fields) . ") VALUES (" . implode(', ', $ph) . ")")
            ->execute($params);
        $this->flash('记录已新增', 'ok');
        $this->redirect("admin/db/browse/$table");
        return '';
    }

    private function doDelete(?string $table, $id): string
    {
        if (!$table || !$this->validTable($table)) { $this->flash('表不存在', 'err'); return $this->tablesList(); }
        $pk = $this->pkOf($table);
        Db::pdo()->prepare("DELETE FROM `{$table}` WHERE `{$pk}` = ?")->execute([$id]);
        $this->flash('记录已删除', 'ok');
        $this->redirect("admin/db/browse/$table");
        return '';
    }

    /* ---------------- 工具方法 ---------------- */

    /** 全部表名白名单（校验来自 URL 的表名） */
    private function tableList(): array
    {
        return Db::pdo()->query("SHOW TABLES")->fetchAll(\PDO::FETCH_COLUMN);
    }

    private function validTable(string $t): bool
    {
        return in_array($t, $this->tableList(), true);
    }

    private function pkOf(string $t): ?string
    {
        $keys = Db::pdo()->query("SHOW KEYS FROM `{$t}` WHERE Key_name='PRIMARY'")->fetchAll();
        if ($keys) return $keys[0]['Column_name'];
        $cols = $this->columnsOf($t);
        return $cols[0]['Field'] ?? null;
    }

    private function columnsOf(string $t): array
    {
        return Db::pdo()->query("DESCRIBE `{$t}`")->fetchAll();
    }

    /** file 模式下可用的数据文件（只读提示） */
    private function fileDataFiles(): array
    {
        $dir = Db::fileDir();
        $out = [];
        foreach (glob($dir . '/*.json') as $f) {
            $out[] = ['name' => basename($f), 'size' => filesize($f)];
        }
        return $out;
    }
}
