package com.ttstd.signaling.controller;

import com.ttstd.signaling.manager.ConnectionRequestManager;
import com.ttstd.signaling.manager.SessionManager;
import com.ttstd.signaling.manager.SignalMetrics;
import com.ttstd.signaling.model.AbuseReport;
import com.ttstd.signaling.model.AdminAccount;
import com.ttstd.signaling.model.DeviceAccount;
import com.ttstd.signaling.model.DeviceBinding;
import com.ttstd.signaling.model.DeviceInfo;
import com.ttstd.signaling.model.UserAccount;
import com.ttstd.signaling.service.AbuseReportService;
import com.ttstd.signaling.service.AccountService;
import com.ttstd.signaling.service.AdminService;
import com.ttstd.signaling.service.AuditService;
import com.ttstd.signaling.service.BindingService;
import com.ttstd.signaling.service.DeviceIdentityService;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.time.Instant;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 后台管理 REST 接口：为 WebRTCSignalServerWeb 提供服务器运行态查询。
 * 所有接口均以 {@code /api/admin} 为前缀，并由 {@link AdminAuthFilter} 校验管理员令牌。
 */
@RestController
@RequestMapping("/api/admin")
public class AdminController {

    private final SessionManager sessionManager;
    private final ConnectionRequestManager connectionRequestManager;
    private final SignalMetrics metrics;
    private final AccountService accountService;
    private final DeviceIdentityService deviceIdentityService;
    private final AuditService auditService;
    private final BindingService bindingService;
    private final AbuseReportService abuseReportService;
    private final AdminService adminService;
    private final long startTime = System.currentTimeMillis();

    public AdminController(SessionManager sessionManager,
                           ConnectionRequestManager connectionRequestManager,
                           SignalMetrics metrics,
                           AccountService accountService,
                           DeviceIdentityService deviceIdentityService,
                           AuditService auditService,
                           BindingService bindingService,
                           AbuseReportService abuseReportService,
                           AdminService adminService) {
        this.sessionManager = sessionManager;
        this.connectionRequestManager = connectionRequestManager;
        this.metrics = metrics;
        this.accountService = accountService;
        this.deviceIdentityService = deviceIdentityService;
        this.auditService = auditService;
        this.bindingService = bindingService;
        this.abuseReportService = abuseReportService;
        this.adminService = adminService;
    }

    /** 仪表盘汇总数据：设备在线情况、待确认连接、流量指标与运行时长。 */
    @GetMapping("/dashboard")
    public Map<String, Object> dashboard() {
        List<DeviceInfo> devices = sessionManager.getAllDeviceDetails();
        long controllers = devices.stream()
                .filter(d -> "CONTROLLER".equals(d.deviceType()) && d.online()).count();
        long controlled = devices.stream()
                .filter(d -> "CONTROLLED".equals(d.deviceType()) && d.online()).count();
        long online = devices.stream().filter(DeviceInfo::online).count();
        long offline = devices.size() - online;

        Map<String, Object> data = new HashMap<>();
        data.put("serverStartTime", startTime);
        data.put("currentTime", System.currentTimeMillis());
        data.put("uptimeMs", System.currentTimeMillis() - startTime);
        data.put("totalDevices", devices.size());
        data.put("onlineDevices", online);
        data.put("offlineDevices", offline);
        data.put("onlineControllers", controllers);
        data.put("onlineControlled", controlled);
        data.put("pendingConnections", connectionRequestManager.getPendingRequests().size());
        data.put("metrics", metrics.snapshot());
        return data;
    }

    /** 设备列表，可按 deviceType（CONTROLLER / CONTROLLED）过滤。 */
    @GetMapping("/devices")
    public List<DeviceInfo> devices(@RequestParam(required = false) String type) {
        List<DeviceInfo> all = sessionManager.getAllDeviceDetails();
        if (type == null || type.isBlank()) {
            return all;
        }
        final String upper = type.toUpperCase();
        return all.stream()
                .filter(d -> upper.equals(d.deviceType()))
                .collect(Collectors.toList());
    }

    /** 当前处于“待被控端确认”状态的连接请求。 */
    @GetMapping("/connections")
    public List<Map<String, Object>> connections() {
        return connectionRequestManager.getPendingRequests();
    }

    /** 服务器连接治理配置项（去重窗口、确认超时、WebSocket 端点等）。 */
    @GetMapping("/config")
    public Map<String, Object> config() {
        Map<String, Object> data = new HashMap<>();
        data.put("dedupWindowMs", connectionRequestManager.getDedupWindowMs());
        data.put("requestTimeoutMs", connectionRequestManager.getRequestTimeoutMs());
        data.put("websocketEndpoint", "/ws/signal");
        data.put("serverPort", 8080);
        return data;
    }

    /** 健康检查。 */
    @GetMapping("/health")
    public Map<String, Object> health() {
        Map<String, Object> data = new HashMap<>();
        data.put("status", "UP");
        data.put("currentTime", System.currentTimeMillis());
        data.put("onlineDevices", sessionManager.getOnlineCount());
        return data;
    }

    // ==================== 管理员登录 ====================

    /**
     * 后台管理员登录，签发独立的管理员令牌（前缀 admt_）。
     * 该接口免鉴权；请求体：{@code {"username": "admin", "password": "***"}}。
     */
    @PostMapping("/login")
    public Map<String, Object> adminLogin(@RequestBody Map<String, Object> body) {
        String username = (String) body.get("username");
        String password = (String) body.get("password");
        return adminService.login(username, password);
    }

    // ==================== 管理员管理 ====================

    /** 当前登录的管理员自身信息。 */
    @GetMapping("/me")
    public Map<String, Object> adminMe(jakarta.servlet.http.HttpServletRequest request) {
        String adminId = currentAdminId(request);
        return Map.of("adminId", adminId);
    }

    /** 管理员列表。 */
    @GetMapping("/admins")
    public List<Map<String, Object>> listAdmins() {
        return adminService.listAdmins().stream().map(AdminController::toAdminView).toList();
    }

    /**
     * 新增后台管理员。
     * 请求体：{@code {"username": "op1", "password": "***", "displayName": "运维1"}}。
     */
    @PostMapping("/admins")
    public Map<String, Object> createAdmin(@RequestBody Map<String, Object> body) {
        String username = (String) body.get("username");
        String password = (String) body.get("password");
        String displayName = (String) body.get("displayName");
        AdminAccount created = adminService.createAdmin(username, password, displayName);
        return Map.of("success", true, "admin", toAdminView(created));
    }

    /** 禁用管理员（保留记录，无法登录）。 */
    @PostMapping("/admins/{adminId}/disable")
    public Map<String, Object> disableAdmin(@PathVariable String adminId) {
        adminService.disableAdmin(adminId);
        return Map.of("success", true, "adminId", adminId);
    }

    /** 启用管理员。 */
    @PostMapping("/admins/{adminId}/enable")
    public Map<String, Object> enableAdmin(@PathVariable String adminId) {
        adminService.enableAdmin(adminId);
        return Map.of("success", true, "adminId", adminId);
    }

    /** 删除管理员（不可删除最后一个启用状态的管理员）。 */
    @DeleteMapping("/admins/{adminId}")
    public Map<String, Object> deleteAdmin(@PathVariable String adminId) {
        adminService.deleteAdmin(adminId);
        return Map.of("success", true, "adminId", adminId);
    }

    /** 重置管理员密码。请求体：{@code {"password": "***"}}。 */
    @PostMapping("/admins/{adminId}/reset-password")
    public Map<String, Object> resetAdminPassword(@PathVariable String adminId,
                                                  @RequestBody Map<String, Object> body) {
        String password = (String) body.get("password");
        if (password == null || password.isBlank()) {
            return Map.of("success", false, "message", "新密码不能为空");
        }
        adminService.resetPassword(adminId, password);
        return Map.of("success", true, "adminId", adminId);
    }

    private String currentAdminId(jakarta.servlet.http.HttpServletRequest request) {
        Object principal = request.getAttribute("adminPrincipal");
        if (principal instanceof com.ttstd.signaling.config.AdminAuthPrincipal p) {
            return p.getAdminId();
        }
        throw com.ttstd.signaling.security.AuthException.unauthorized("未识别管理员身份");
    }

    private static Map<String, Object> toAdminView(AdminAccount a) {
        Map<String, Object> item = new LinkedHashMap<>();
        item.put("adminId", a.getAdminId());
        item.put("username", a.getUsername());
        item.put("displayName", a.getDisplayName());
        item.put("status", a.getStatus().name());
        item.put("createdAt", a.getCreatedAt().toEpochMilli());
        item.put("lastLoginAt", a.getLastLoginAt() == null ? null : a.getLastLoginAt().toEpochMilli());
        return item;
    }

    // ==================== 账号管理 ====================

    /**
     * 主控端账号列表，支持按用户名关键字与状态过滤。
     *
     * @param keyword 用户名模糊匹配，忽略大小写
     * @param status  账号状态：ACTIVE / SUSPENDED / BANNED
     */
    @GetMapping("/users")
    public List<Map<String, Object>> users(@RequestParam(required = false) String keyword,
                                           @RequestParam(required = false) String status) {
        return accountService.listUsers().stream()
                .filter(u -> keyword == null || keyword.isBlank()
                        || u.getUsername().toLowerCase().contains(keyword.toLowerCase()))
                .filter(u -> status == null || status.isBlank()
                        || u.getStatus().name().equalsIgnoreCase(status))
                .map(AdminController::toUserView)
                .toList();
    }

    /**
     * 新增账号。
     *
     * <p>请求体：{@code {"username": "alice", "password": "***"}}
     *
     * <p>此处创建的是普通用户（app_user），与管理员（admin_account）完全隔离。
     * 若需创建后台管理员，请调用 {@code POST /api/admin/admins}。
     */
    @PostMapping("/users")
    public Map<String, Object> createUser(@RequestBody Map<String, Object> body) {
        String username = (String) body.get("username");
        String password = (String) body.get("password");
        if (username == null || username.isBlank() || password == null || password.isBlank()) {
            return Map.of("success", false, "message", "用户名与密码不能为空");
        }
        UserAccount created = accountService.adminCreateUser(username.trim(), password);
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("success", true);
        result.put("user", toUserView(created));
        return result;
    }

    /** 删除账号，同时下线其全部会话。 */
    @DeleteMapping("/users/{userId}")
    public Map<String, Object> deleteUser(@PathVariable String userId) {
        accountService.deleteUser(userId);
        return Map.of("success", true, "userId", userId);
    }

    /** 管理员重置账号密码。请求体：{@code {"password": "***"}} */
    @PostMapping("/users/{userId}/reset-password")
    public Map<String, Object> resetPassword(@PathVariable String userId,
                                             @RequestBody Map<String, Object> body) {
        String password = (String) body.get("password");
        if (password == null || password.isBlank()) {
            return Map.of("success", false, "message", "新密码不能为空");
        }
        accountService.adminResetPassword(userId, password);
        return Map.of("success", true, "userId", userId);
    }

    /** 指定账号的活跃登录会话。 */
    @GetMapping("/users/{userId}/sessions")
    public List<Map<String, Object>> userSessions(@PathVariable String userId) {
        return accountService.listSessions(userId).stream().map(s -> {
            Map<String, Object> item = new LinkedHashMap<>();
            item.put("sessionId", s.getSessionId());
            item.put("ip", s.getIp());
            item.put("userAgent", s.getUserAgent());
            item.put("createdAt", s.getCreatedAt().toEpochMilli());
            item.put("lastSeenAt", s.getLastSeenAt().toEpochMilli());
            return item;
        }).toList();
    }

    /**
     * 封禁账号并立即下线其全部连接。
     *
     * <p>请求体：{@code {"reason": "...", "durationSeconds": 3600}}，
     * durationSeconds 缺省或 <=0 表示永久封禁。
     */
    @PostMapping("/users/{userId}/ban")
    public Map<String, Object> banUser(@PathVariable String userId,
                                       @RequestBody(required = false) Map<String, Object> body) {
        String reason = body == null ? null : (String) body.get("reason");
        long duration = body == null ? 0 : toLong(body.get("durationSeconds"));
        Instant until = duration > 0 ? Instant.now().plusSeconds(duration) : null;
        accountService.ban(userId, until, reason);
        return Map.of("success", true, "userId", userId,
                "permanent", until == null);
    }

    /** 解封账号。 */
    @PostMapping("/users/{userId}/unban")
    public Map<String, Object> unbanUser(@PathVariable String userId) {
        accountService.unban(userId);
        return Map.of("success", true, "userId", userId);
    }

    /** 强制账号全端下线（不改变封禁状态）。 */
    @PostMapping("/users/{userId}/kick")
    public Map<String, Object> kickUser(@PathVariable String userId,
                                        @RequestBody(required = false) Map<String, Object> body) {
        String reason = body == null ? "管理员强制下线" : (String) body.getOrDefault("reason", "管理员强制下线");
        accountService.revokeAllSessions(userId, reason);
        return Map.of("success", true, "userId", userId);
    }

    /** 踢出指定登录会话。 */
    @PostMapping("/sessions/{sessionId}/kick")
    public Map<String, Object> kickSession(@PathVariable String sessionId,
                                           @RequestBody(required = false) Map<String, Object> body) {
        String reason = body == null ? "管理员强制下线" : (String) body.getOrDefault("reason", "管理员强制下线");
        accountService.revokeSession(sessionId, reason);
        return Map.of("success", true, "sessionId", sessionId);
    }

    // ==================== 设备管理 ====================

    /**
     * 已激活的被控端设备列表（SN 已脱敏）。
     *
     * @param keyword 按设备 UID / 型号模糊匹配
     * @param status  设备状态：ACTIVE / SUSPENDED / BANNED
     * @param online  在线状态过滤，null 表示不过滤
     */
    @GetMapping("/device-accounts")
    public List<Map<String, Object>> deviceAccounts(@RequestParam(required = false) String keyword,
                                                    @RequestParam(required = false) String status,
                                                    @RequestParam(required = false) Boolean online) {
        return deviceIdentityService.listDevices().stream()
                .filter(d -> keyword == null || keyword.isBlank()
                        || d.getDeviceUid().toLowerCase().contains(keyword.toLowerCase())
                        || (d.getModel() != null && d.getModel().toLowerCase().contains(keyword.toLowerCase())))
                .filter(d -> status == null || status.isBlank()
                        || d.getStatus().name().equalsIgnoreCase(status))
                .filter(d -> online == null || online == sessionManager.isDeviceOnline(d.getDeviceUid()))
                .map(this::toDeviceView)
                .toList();
    }

    /** 强制设备下线：吊销现有令牌并断开连接，但不改变设备状态。 */
    @PostMapping("/device-accounts/{deviceUid}/kick")
    public Map<String, Object> kickDevice(@PathVariable String deviceUid,
                                          @RequestBody(required = false) Map<String, Object> body) {
        String reason = body == null ? "管理员强制下线"
                : (String) body.getOrDefault("reason", "管理员强制下线");
        deviceIdentityService.kick(deviceUid, reason);
        return Map.of("success", true, "deviceUid", deviceUid);
    }

    /** 删除设备记录并断开其连接。 */
    @DeleteMapping("/device-accounts/{deviceUid}")
    public Map<String, Object> deleteDevice(@PathVariable String deviceUid) {
        deviceIdentityService.delete(deviceUid);
        return Map.of("success", true, "deviceUid", deviceUid);
    }

    private Map<String, Object> toDeviceView(DeviceAccount d) {
        Map<String, Object> item = new LinkedHashMap<>();
        item.put("deviceUid", d.getDeviceUid());
        item.put("sn", d.maskedSn());
        item.put("model", d.getModel());
        item.put("status", d.getStatus().name());
        item.put("statusReason", d.getStatusReason());
        item.put("statusUntil", d.getStatusUntil() == null ? null : d.getStatusUntil().toEpochMilli());
        item.put("provisionedAt", d.getProvisionedAt().toEpochMilli());
        item.put("lastOnlineAt", d.getLastOnlineAt() == null ? null : d.getLastOnlineAt().toEpochMilli());
        item.put("online", sessionManager.isDeviceOnline(d.getDeviceUid()));
        return item;
    }

    /** 禁用设备并立即断开其连接。 */
    @PostMapping("/device-accounts/{deviceUid}/disable")
    public Map<String, Object> disableDevice(@PathVariable String deviceUid,
                                             @RequestBody(required = false) Map<String, Object> body) {
        String reason = body == null ? null : (String) body.get("reason");
        long duration = body == null ? 0 : toLong(body.get("durationSeconds"));
        Instant until = duration > 0 ? Instant.now().plusSeconds(duration) : null;
        deviceIdentityService.disable(deviceUid, until, reason);
        return Map.of("success", true, "deviceUid", deviceUid);
    }

    /** 启用设备。 */
    @PostMapping("/device-accounts/{deviceUid}/enable")
    public Map<String, Object> enableDevice(@PathVariable String deviceUid) {
        deviceIdentityService.enable(deviceUid);
        return Map.of("success", true, "deviceUid", deviceUid);
    }

    /** 批量导入 SN 白名单。请求体：{@code {"sns": ["SN001", "SN002"]}} */
    @PostMapping("/device-allowlist")
    public Map<String, Object> importAllowlist(@RequestBody Map<String, Object> body) {
        Object raw = body.get("sns");
        if (!(raw instanceof List<?> list)) {
            return Map.of("success", false, "message", "缺少 sns 数组");
        }
        List<String> sns = list.stream().map(String::valueOf).toList();
        int added = deviceIdentityService.importAllowlist(sns);
        return Map.of("success", true, "added", added,
                "total", deviceIdentityService.getAllowlist().size());
    }

    /** 查询 SN 白名单。 */
    @GetMapping("/device-allowlist")
    public Map<String, Object> listAllowlist() {
        List<String> sns = deviceIdentityService.getAllowlist().stream().sorted().toList();
        return Map.of("total", sns.size(), "sns", sns);
    }

    /** 将指定 SN 移出白名单。 */
    @DeleteMapping("/device-allowlist/{sn}")
    public Map<String, Object> removeAllowlist(@PathVariable String sn) {
        boolean removed = deviceIdentityService.removeFromAllowlist(sn);
        return Map.of("success", removed, "sn", sn,
                "total", deviceIdentityService.getAllowlist().size());
    }

    // ==================== 用户绑定关系 ====================

    /** 列出某用户账号绑定的全部终端设备（含已撤销），并附在线状态。 */
    @GetMapping("/users/{userId}/bindings")
    public List<Map<String, Object>> userBindings(@PathVariable String userId) {
        return bindingService.listByUser(userId).stream().map(b -> {
            Map<String, Object> item = new LinkedHashMap<>();
            item.put("bindingId", b.getBindingId());
            item.put("deviceUid", b.getDeviceUid());
            item.put("userId", b.getUserId());
            item.put("role", b.getRole().name());
            item.put("alias", b.getAlias());
            item.put("status", b.getStatus().name());
            item.put("online", sessionManager.isDeviceOnline(b.getDeviceUid()));
            item.put("boundAt", b.getBoundAt() == null ? null : b.getBoundAt().toEpochMilli());
            return item;
        }).toList();
    }

    // ==================== 绑定 / 黑名单管理 ====================

    /** 列出某设备的全部绑定关系（含已撤销）。 */
    @GetMapping("/devices/{deviceUid}/bindings")
    public List<Map<String, Object>> deviceBindings(@PathVariable String deviceUid) {
        return bindingService.listByDevice(deviceUid).stream().map(b -> {
            Map<String, Object> item = new LinkedHashMap<>();
            item.put("bindingId", b.getBindingId());
            item.put("deviceUid", b.getDeviceUid());
            item.put("userId", b.getUserId());
            item.put("role", b.getRole().name());
            item.put("alias", b.getAlias());
            item.put("status", b.getStatus().name());
            item.put("boundAt", b.getBoundAt().toEpochMilli());
            return item;
        }).toList();
    }

    /** 列出某设备的黑名单。 */
    @GetMapping("/devices/{deviceUid}/blacklist")
    public List<Map<String, Object>> deviceBlacklist(@PathVariable String deviceUid) {
        return bindingService.listBlacklist(deviceUid).stream().map(e -> {
            Map<String, Object> item = new LinkedHashMap<>();
            item.put("blockedUserId", e.getBlockedUserId());
            item.put("reason", e.getReason());
            item.put("createdAt", e.getCreatedAt().toEpochMilli());
            return item;
        }).toList();
    }

    /**
     * 管理员建立绑定。
     * 请求体：{@code {"username": "alice", "role": "MEMBER", "alias": "客厅电视"}}。
     */
    @PostMapping("/devices/{deviceUid}/bind")
    public Map<String, Object> adminBind(@PathVariable String deviceUid,
                                         @RequestBody Map<String, Object> body) {
        String username = (String) body.get("username");
        if (username == null || username.isBlank()) {
            return Map.of("success", false, "message", "缺少 username");
        }
        String userId = bindingService.resolveUserId(username);
        DeviceBinding.BindingRole role = parseRole(body.get("role"));
        String alias = (String) body.get("alias");
        DeviceBinding binding = bindingService.bind(deviceUid, userId, role, alias, "admin");
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("success", true);
        result.put("bindingId", binding.getBindingId());
        result.put("deviceUid", deviceUid);
        result.put("userId", userId);
        return result;
    }

    /** 管理员解绑（按 username）。 */
    @PostMapping("/devices/{deviceUid}/unbind")
    public Map<String, Object> adminUnbind(@PathVariable String deviceUid,
                                           @RequestBody Map<String, Object> body) {
        String username = (String) body.get("username");
        if (username == null || username.isBlank()) {
            return Map.of("success", false, "message", "缺少 username");
        }
        String userId = bindingService.resolveUserId(username);
        bindingService.revokeBinding(deviceUid, userId, "admin");
        return Map.of("success", true, "deviceUid", deviceUid, "userId", userId);
    }

    /** 管理员将某账号加入设备黑名单。请求体：{@code {"username": "alice", "reason": "骚扰"}}。 */
    @PostMapping("/devices/{deviceUid}/blacklist")
    public Map<String, Object> adminBlacklist(@PathVariable String deviceUid,
                                              @RequestBody Map<String, Object> body) {
        String username = (String) body.get("username");
        if (username == null || username.isBlank()) {
            return Map.of("success", false, "message", "缺少 username");
        }
        String userId = bindingService.resolveUserId(username);
        String reason = (String) body.get("reason");
        bindingService.addBlacklist(deviceUid, userId, reason, "admin");
        return Map.of("success", true, "deviceUid", deviceUid, "userId", userId);
    }

    /** 管理员移除设备黑名单。请求体：{@code {"username": "alice"}}。 */
    @PostMapping("/devices/{deviceUid}/unblacklist")
    public Map<String, Object> adminUnblacklist(@PathVariable String deviceUid,
                                                @RequestBody Map<String, Object> body) {
        String username = (String) body.get("username");
        if (username == null || username.isBlank()) {
            return Map.of("success", false, "message", "缺少 username");
        }
        String userId = bindingService.resolveUserId(username);
        bindingService.removeBlacklist(deviceUid, userId, "admin");
        return Map.of("success", true, "deviceUid", deviceUid, "userId", userId);
    }

    private static DeviceBinding.BindingRole parseRole(Object value) {
        if (value == null) {
            return DeviceBinding.BindingRole.MEMBER;
        }
        try {
            return DeviceBinding.BindingRole.valueOf(value.toString().toUpperCase());
        } catch (IllegalArgumentException e) {
            return DeviceBinding.BindingRole.MEMBER;
        }
    }

    // ==================== 骚扰举报管理 ====================

    /** 列出举报。可按状态过滤，亦可按被举报用户过滤。 */
    @GetMapping("/abuse-reports")
    public List<AbuseReport> listAbuseReports(
            @RequestParam(required = false) String status,
            @RequestParam(required = false) String reportedUserId) {
        if (reportedUserId != null && !reportedUserId.isBlank()) {
            return abuseReportService.listByReportedUser(reportedUserId);
        }
        if (status != null && !status.isBlank()) {
            try {
                AbuseReport.ReportStatus s = AbuseReport.ReportStatus.valueOf(status.toUpperCase());
                return abuseReportService.listByReportedUser(null).stream()
                        .filter(r -> r.getStatus() == s).toList();
            } catch (IllegalArgumentException ignored) {
                // 落入默认
            }
        }
        return abuseReportService.listByReportedUser(null);
    }

    /** 处理举报：标记 HANDLED / DISMISSED（并可联动封禁被举报账号）。 */
    @PostMapping("/abuse-reports/{id}/handle")
    public Map<String, Object> handleAbuseReport(@PathVariable Long id,
                                                @RequestBody Map<String, Object> body) {
        String statusStr = (String) body.get("status");
        AbuseReport.ReportStatus status = AbuseReport.ReportStatus.HANDLED;
        if (statusStr != null) {
            try {
                status = AbuseReport.ReportStatus.valueOf(statusStr.toUpperCase());
            } catch (IllegalArgumentException ignored) {
                // 保持默认
            }
        }
        abuseReportService.handle(id, status);
        Map<String, Object> r = new LinkedHashMap<>();
        r.put("success", true);
        r.put("id", id);
        r.put("status", status.name());
        return r;
    }

    // ==================== 审计日志 ====================

    /**
     * 查询审计日志。
     *
     * <p>支持可选过滤：{@code actorId}（账号/设备/管理员 ID）、
     * {@code action}（动作常量，如 LOGIN、BAN）、{@code result}（SUCCESS/FAILURE）。
     * 默认返回最近 100 条，最多 1000 条。
     */
    @GetMapping("/audit")
    public Map<String, Object> audit(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "100") int limit,
            @RequestParam(required = false) String actorId,
            @RequestParam(required = false) String action,
            @RequestParam(required = false) String result) {
        Page<AuditService.AuditEntry> found =
                auditService.search(actorId, action, result, page, limit);
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("items", found.getContent());
        body.put("page", found.getNumber());
        body.put("size", found.getSize());
        body.put("total", found.getTotalElements());
        body.put("totalPages", found.getTotalPages());
        return body;
    }

    private static Map<String, Object> toUserView(UserAccount u) {
        Map<String, Object> item = new LinkedHashMap<>();
        item.put("userId", u.getUserId());
        item.put("username", u.getUsername());
        item.put("status", u.getStatus().name());
        item.put("statusReason", u.getStatusReason());
        item.put("statusUntil", u.getStatusUntil() == null ? null : u.getStatusUntil().toEpochMilli());
        item.put("locked", u.isLocked());
        item.put("createdAt", u.getCreatedAt().toEpochMilli());
        item.put("lastLoginAt", u.getLastLoginAt() == null ? null : u.getLastLoginAt().toEpochMilli());
        return item;
    }

    private static long toLong(Object value) {
        return value instanceof Number n ? n.longValue() : 0L;
    }
}
