<?php
// +----------------------------------------------------------------------
// | onezan-admin 快速开发的管理平台
// +----------------------------------------------------------------------
// | 官方网址：https://www.onezan.cn
// +----------------------------------------------------------------------
// | onezan团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: 543885307@qq.com
// +----------------------------------------------------------------------
namespace app\service;

use think\facade\Db;
use think\Request;


/**
 * 管理员操作日志服务
 * @package app\service
 */
class AdminOperationLogService
{
    /**
     * 构建操作日志数据结构
     * @param Request $request
     * @param string $adminUsername
     * @param int $statusCode
     * @return array
     */
    public static function buildPayload(Request $request, string $adminUsername, int $statusCode): array
    {
        $params = [];
        try {
            $params = $request->param();
        } catch (\Throwable $e) {
            $params = [];
        }

        $safe = self::sanitize($params);
        $encoded = json_encode($safe, JSON_UNESCAPED_UNICODE);
        if (!is_string($encoded)) $encoded = '';
        if (strlen($encoded) > 8000) {
            $encoded = substr($encoded, 0, 8000);
        }

        $path = '';
        try {
            $path = (string)$request->pathinfo();
        } catch (\Throwable $e) {
            $path = '';
        }

        $method = '';
        try {
            $method = strtoupper((string)$request->method());
        } catch (\Throwable $e) {
            $method = '';
        }

        $ip = '';
        try {
            $ip = (string)$request->ip();
        } catch (\Throwable $e) {
            $ip = '';
        }

        return [
            'admin_username' => $adminUsername,
            'method' => $method,
            'path' => $path,
            'action' => self::guessAction($request),
            'request_data' => $encoded,
            'ip' => $ip,
            'status_code' => $statusCode,
            'create_time' => time(),
        ];
    }


     /**
     * 记录操作日志
     * @param array $payload
     */

    public static function record(array $payload): void
    {
        try {
            Db::name('sys_admin_operation_logs')->insert($payload);
        } catch (\Throwable $e) {
        }
    }

     /**
     * 猜测操作
     * @param Request $request
     * @return string
     */

    private static function guessAction(Request $request): string
    {
        try {
            $path = (string)$request->pathinfo();
        } catch (\Throwable $e) {
            $path = '';
        }
        if ($path === '') return '';

        $segments = array_values(array_filter(explode('/', str_replace('\\', '/', $path))));
        $last = $segments ? $segments[count($segments) - 1] : '';
        $secondLast = count($segments) >= 2 ? $segments[count($segments) - 2] : '';

        $method = '';
        try {
            $method = strtoupper((string)$request->method());
        } catch (\Throwable $e) {
            $method = '';
        }

        $key = $secondLast !== '' ? ($secondLast . '.' . $last) : $last;
        if ($method === 'GET') return $key . '.view';
        if ($method === 'POST') return $key . '.action';
        if ($method === 'PUT') return $key . '.action';
        if ($method === 'DELETE') return $key . '.action';
        return $key;
    }

     /**
     * 清理敏感数据
     * @param mixed $value
     * @return mixed
     */
    private static function sanitize($value)
    {
        if (is_array($value)) {
            $next = [];
            foreach ($value as $k => $v) {
                $key = is_string($k) ? strtolower($k) : $k;
                if (
                    (is_string($key) && strpos($key, 'password') !== false) ||
                    in_array($key, ['password', 'pwd', 'token', 'authorization'], true)
                ) {
                    $next[$k] = '***';
                    continue;
                }
                $next[$k] = self::sanitize($v);
            }
            return $next;
        }

        if (is_object($value)) {
            return '[object]';
        }

        if (is_string($value)) {
            if (strlen($value) > 2000) return substr($value, 0, 2000);
            return $value;
        }

        if (is_numeric($value) || is_bool($value) || $value === null) {
            return $value;
        }

        return (string)$value;
    }
}