<?php
declare(strict_types=1);

/**
 * 控制器基类
 * 统一 JSON 输出规范，并提供 callBefore/callEnd 钩子约定
 */
abstract class Action
{
    /** 当前请求参数，由 Mvc::dispatch() 注入 */
    protected array $request = [];

    /**
     * 由 Mvc::dispatch() 调用，注入当前请求参数
     */
    public function setRequest(array $request): void
    {
        $this->request = $request;
    }

    /**
     * 当前 Action 是否要求登录，默认 true（必须登录才能访问）
     * 登录页/验证码/找回密码相关的 Action 需要覆盖此方法返回 false
     */
    protected function requiresLogin(): bool
    {
        return true;
    }

    /**
     * 全站登录态校验，由 Mvc::dispatch() 在绑定参数之后、调用具体方法之前自动调用
     * 没登录时：ajax 请求（jQuery 默认带 X-Requested-With）返回 401 JSON，普通页面请求 302 跳登录页
     */
    public function callBefore(string $method, array $params): void
    {
        if (PHP_SAPI !== 'cli' && session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }

        if (!$this->requiresLogin()) {
            return;
        }

        if (Auth::user() !== null || Auth::tryRememberLogin()) {
            return;
        }

        if (($_SERVER['HTTP_X_REQUESTED_WITH'] ?? '') === 'XMLHttpRequest') {
            $this->error(401, '请先登录');
        }

        header('Location: ' . HOST . '/login/index');
        exit;
    }

    /**
     * 成功响应并终止程序
     */
    protected function success(mixed $data = null, string $msg = ''): void
    {
        $this->json(0, $data, $msg);
    }

    /**
     * 失败响应并终止程序
     * @param int $code 非 0 的错误代码
     */
    protected function error(int $code, string $msg = ''): void
    {
        if ($code === 0) {
            throw new MvcException('0 为成功代码，不能用于错误响应');
        }
        $this->json($code, null, $msg);
    }

    /**
     * 输出统一格式的 JSON 并终止程序
     * 响应结构: {"code":0,"data":{...},"msg":""}
     */
    protected function json(int $code, mixed $data = null, string $msg = ''): void
    {
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode([
            'code' => $code,
            'data' => $data,
            'msg'  => $msg,
        ], JSON_UNESCAPED_UNICODE);
        exit();
    }
}
