<?php
namespace app\adminVue\controller;

use app\BaseController;
use think\facade\Cache;
use think\facade\Db;
use think\exception\HttpResponseException;

/** Shared authentication and action policy. No dependency on the old TokenCheck. */
abstract class V2Base extends BaseController
{
    protected $actor = [];
    protected $middleware = [V2ApiMiddleware::class];

    protected function initialize()
    {
        $action = strtolower($this->request->action());
        $class = (new \ReflectionClass($this))->getShortName();
        if ($class === 'V2Auth' && in_array($action, ['login', 'health'], true)) return;
        $token = $this->requestToken();
        $session = $token ? Cache::get('admin-v2:session:'.hash('sha256', $token)) : null;
        if (!$session || $session['expiresAt'] <= time()) $this->fail('登录已过期，请重新登录', 401);
        $this->actor = Db::name('admin_user')->where(['id'=>$session['id'], 'status'=>1])->find() ?: [];
        if (!$this->actor) $this->fail('账号不可用，请重新登录', 401);
        if (!isset($session['credential']) || !hash_equals($session['credential'],hash('sha256',(string)$this->actor['password']))) $this->fail('登录凭据已变化，请重新登录',401);
        $actions = ['V2Auth'=>['me'=>'GET','menus'=>'GET','buttons'=>'GET','logout'=>'POST'],'V2Workbench'=>['overview'=>'GET'],'V2Menu'=>['index'=>'GET','update'=>'POST'],'V2Account'=>['index'=>'GET','options'=>'GET','detail'=>'GET','preview'=>'POST','import'=>'POST']];
        $actions['V2DailyIssue']=['index'=>'GET','options'=>'GET','detail'=>'GET','export'=>'GET','exportimage'=>'GET'];
        $actions['V2AfterSalesRepair']=['index'=>'GET','options'=>'GET','detail'=>'GET','export'=>'GET','exportimage'=>'GET'];
        $actions['V2Suggestion']=['index'=>'GET','options'=>'GET','detail'=>'GET','image'=>'GET','export'=>'GET','assign'=>'POST','assignersettings'=>'GET','saveassigners'=>'POST','departmentsettings'=>'GET','savedepartment'=>'POST'];
        $actions['V2Employee']=['index'=>'GET','options'=>'GET','detail'=>'GET','update'=>'POST','preview'=>'POST','import'=>'POST'];
        $actions['V2Performance']=['index'=>'GET','options'=>'GET','detail'=>'GET','employee'=>'GET','summary'=>'GET','export'=>'GET','image'=>'GET','create'=>'POST','remove'=>'POST'];
        $actions['V2Leave']=['index'=>'GET','options'=>'GET','detail'=>'GET','employee'=>'GET','export'=>'GET','preview'=>'POST','commit'=>'POST'];
        $actions['V2ExamSet']=['index'=>'GET','save'=>'POST'];
        $actions['V2Question']=['options'=>'GET','index'=>'GET','preview'=>'POST','import'=>'POST','update'=>'POST','remove'=>'POST'];
        $actions['V2TemplateFailures']=['index'=>'GET','overview'=>'GET','detail'=>'GET','resolve'=>'POST','subscribe'=>'POST','retryalerts'=>'POST'];
        $actions['V2HrRecords']=['exportimage'=>'GET','index'=>'GET','options'=>'GET','detail'=>'GET','export'=>'GET'];
        $actions['V2OutplantSettings']=['index'=>'GET','save'=>'POST'];
        $actions['V2MobileMenu']=['index'=>'GET','save'=>'POST'];
        $actions['V2RadioSettings']=['index'=>'GET','candidates'=>'GET','records'=>'GET','grant'=>'POST','revoke'=>'POST'];
        $actions['V2Inventory']=['index'=>'GET','options'=>'GET','upload'=>'POST','commit'=>'POST','discard'=>'POST','update'=>'POST','deletepreview'=>'POST','remove'=>'POST'];
        $actions['V2MachineCheck']=['positionoptions'=>'GET','positiondata'=>'GET','positionsave'=>'POST','positionlogs'=>'GET','index'=>'GET','options'=>'GET','upload'=>'POST','preview'=>'GET','commit'=>'POST','discard'=>'POST'];
        $actions['V2CheckIssue']=['index'=>'GET','options'=>'GET','settings'=>'GET','savesettings'=>'POST'];
        $actions['V2InventoryRecord']=['index'=>'GET','event'=>'GET','saveevent'=>'POST','update'=>'POST','recheckpreview'=>'POST','recheck'=>'POST','maintenancepreview'=>'POST','maintenance'=>'POST','export'=>'GET'];
        $actions['V2Training']=['options'=>'GET','employee'=>'GET','templates'=>'GET','savetemplate'=>'POST','sorttemplate'=>'POST','removetemplate'=>'POST','registrations'=>'GET','registration'=>'GET','plans'=>'GET','plan'=>'GET','saveplan'=>'POST','publish'=>'POST','course'=>'GET','makeup'=>'POST','release'=>'POST','exam'=>'POST'];
        $method = $actions[$class][$action] ?? null;
        if (!$method) $this->fail('接口未开放', 404);
        if ($this->request->method() !== $method) $this->fail('请求方法不正确', 405);
    }

    protected function fail($message, $status = 422)
    {
        throw new HttpResponseException(json(['code'=>$status, 'msg'=>$message, 'data'=>null], $status));
    }

    protected function requestToken()
    {
        // Apache/FastCGI may strip Authorization. This separate header survives phpstudy.
        $custom = $this->request->header('x-admin-token', '');
        if ($custom !== '') return preg_match('/^[a-f0-9]{64}$/D', $custom) ? $custom : '';
        $header = $this->request->header('authorization', '');
        return preg_match('/^Bearer ([a-f0-9]{64})$/D', $header, $match) ? $match[1] : '';
    }

    protected function respond($payload)
    {
        $status = http_response_code() ?: 200;
        if (is_string($payload)) $payload = json_decode($payload, true);
        if (!is_array($payload)) $this->fail('接口响应格式错误', 500);
        $failed = isset($payload['code']) && !in_array((int)$payload['code'], [0,200], true);
        if ($failed && $status < 400) $status = 422;
        if (in_array($status, [440,441], true)) $status = 422;
        if ($status === 470) $status = 401;
        $payload['code'] = $status >= 400 ? $status : 0;
        $payload['msg'] = strip_tags($payload['msg'] ?? '');
        if (!array_key_exists('data', $payload)) $payload['data'] = null;
        $payload = self::redact($payload);
        throw new HttpResponseException(json($payload, $status));
    }

    // Query services return through this method; never leak password/token columns from SELECT *.
    public static function redact($value)
    {
        if (!is_array($value)) return $value;
        $safe = [];
        foreach ($value as $key=>$item) {
            if (is_string($key) && in_array(strtolower($key), ['password','oldpassword','repassword','token','openid','gzhopenid'], true)) continue;
            $safe[$key] = self::redact($item);
        }
        return $safe;
    }
}
