import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Put,
  Query,
  Req,
} from "@nestjs/common";
import { NotificationsService } from "./notifications.service.js";
import {
  sendNotificationSchema,
  sendNotificationBatchSchema,
  batchMarkAsReadSchema,
  markAllAsReadSchema,
  recallNotificationSchema,
} from "./notifications.dto.js";
import type {
  SendNotificationDto,
  SendNotificationBatchDto,
} from "./notifications.dto.js";
import {
  Permissions,
  RequirePermission,
} from "../middleware/permission.guard.js";
import type { AuthenticatedRequest } from "../middleware/auth.middleware.js";
import { PermissionDeniedError } from "../shared/errors/application-error.js";

/**
 * NotificationsController —— REST API 入口。
 *
 * 对齐 02-architecture-design.md §4.1 REST API 设计：
 * - POST /notifications/send（单条发送）
 * - POST /notifications/batch（批量发送）
 * - GET /notifications/user/:userId（列表，支持分页+过滤）
 * - GET /notifications/user/:userId/unread-count（未读数）
 * - PUT /notifications/:id/read（标记已读）
 * - PUT /notifications/batch/read（批量标记已读）
 * - PUT /notifications/read-all（全部已读）
 * - GET /notifications/search（ES 全文检索）
 * - POST /notifications/recall（撤回广播）
 * - DELETE /notifications/:id（删除）
 */
@Controller("notifications")
export class NotificationsController {
  constructor(private readonly service: NotificationsService) {}

  @Post("send")
  @RequirePermission(Permissions.MSG_NOTIFICATION_SEND)
  async send(@Body() body: unknown): Promise<{ success: true; data: unknown }> {
    const dto: SendNotificationDto = sendNotificationSchema.parse(body);
    const result = await this.service.send(dto);
    return { success: true, data: result };
  }

  @Post("batch")
  @RequirePermission(Permissions.MSG_NOTIFICATION_SEND)
  async sendBatch(
    @Body() body: unknown,
  ): Promise<{ success: true; data: unknown }> {
    const dto: SendNotificationBatchDto =
      sendNotificationBatchSchema.parse(body);
    const result = await this.service.sendBatch(dto);
    return { success: true, data: result };
  }

  @Get("user/:userId")
  @RequirePermission(Permissions.MSG_NOTIFICATION_READ)
  async listByUser(
    @Param("userId") userId: string,
    @Query("onlyUnread") onlyUnread: string,
    @Query("type") type: string,
    @Query("page") page: string,
    @Query("pageSize") pageSize: string,
  ): Promise<{ success: true; data: unknown }> {
    const result = await this.service.listByUser(userId, {
      onlyUnread: onlyUnread === "true",
      type: type || undefined,
      page: Number(page) > 0 ? Number(page) : 1,
      pageSize: Number(pageSize) > 0 ? Number(pageSize) : 20,
    });
    return { success: true, data: result };
  }

  @Get("user/:userId/unread-count")
  @RequirePermission(Permissions.MSG_NOTIFICATION_READ)
  async getUnreadCount(
    @Param("userId") userId: string,
  ): Promise<{ success: true; data: { count: number } }> {
    const count = await this.service.getUnreadCount(userId);
    return { success: true, data: { count } };
  }

  @Put(":id/read")
  @RequirePermission(Permissions.MSG_NOTIFICATION_READ)
  async markAsRead(
    @Param("id") id: string,
    @Body() body: unknown,
  ): Promise<{ success: true }> {
    const userId =
      typeof body === "object" && body !== null && "userId" in body
        ? String((body as { userId: unknown }).userId)
        : "";
    if (!userId) {
      throw new PermissionDeniedError("MSG_NOTIFICATION_READ");
    }
    await this.service.markAsRead(id, userId);
    return { success: true };
  }

  @Put("batch/read")
  @RequirePermission(Permissions.MSG_NOTIFICATION_READ)
  async batchMarkAsRead(@Body() body: unknown): Promise<{ success: true }> {
    const dto = batchMarkAsReadSchema.parse(body);
    await this.service.batchMarkAsRead(dto.ids, dto.userId);
    return { success: true };
  }

  @Put("read-all")
  @RequirePermission(Permissions.MSG_NOTIFICATION_READ)
  async markAllAsRead(
    @Body() body: unknown,
  ): Promise<{ success: true; data: { updated: number } }> {
    const dto = markAllAsReadSchema.parse(body);
    const updated = await this.service.markAllAsRead(dto.userId, dto.before);
    return { success: true, data: { updated } };
  }

  @Get("search")
  @RequirePermission(Permissions.MSG_NOTIFICATION_READ)
  async search(
    @Req() req: AuthenticatedRequest,
    @Query("q") q: string,
    @Query("userId") userIdParam: string,
    @Query("type") type: string,
    @Query("page") page: string,
    @Query("pageSize") pageSize: string,
  ): Promise<{ success: true; data: unknown }> {
    const userId = req.userId ?? userIdParam;
    if (!userId) {
      throw new PermissionDeniedError("MSG_NOTIFICATION_READ");
    }
    const result = await this.service.search(userId, q, {
      type: type || undefined,
      page: Number(page) > 0 ? Number(page) : 1,
      pageSize: Number(pageSize) > 0 ? Number(pageSize) : 20,
    });
    return { success: true, data: result };
  }

  @Post("recall")
  @RequirePermission(Permissions.MSG_NOTIFICATION_MANAGE)
  async recall(
    @Body() body: unknown,
  ): Promise<{ success: true; data: { recalledCount: number } }> {
    const dto = recallNotificationSchema.parse(body);
    const recalledCount = await this.service.recall(dto.groupId);
    return { success: true, data: { recalledCount } };
  }

  @Delete(":id")
  @RequirePermission(Permissions.MSG_NOTIFICATION_MANAGE)
  async delete(@Param("id") id: string): Promise<{ success: true }> {
    await this.service.delete(id);
    return { success: true };
  }
}
