"use server"

import { revalidatePath } from "next/cache"
import { createId } from "@paralleldrive/cuid2"
import { getTranslations } from "next-intl/server"

import { requirePermission, PermissionDeniedError } from "@/shared/lib/auth-guard"
import { trackEvent } from "@/shared/lib/track-event"
import { Permissions } from "@/shared/types/permissions"
import type { ActionState } from "@/shared/types/action-state"
import { sendBatchNotifications } from "@/modules/notifications"
import type { NotificationPayload } from "@/modules/notifications"

import { CreateAnnouncementSchema, UpdateAnnouncementSchema } from "./schema"
import {
  getAnnouncements,
  getAnnouncementById,
  insertAnnouncement,
  updateAnnouncementById,
  deleteAnnouncementById,
  publishAnnouncementById,
  archiveAnnouncementById,
  toggleAnnouncementPin,
  markAnnouncementAsRead,
  getAnnouncementReadStatusForUser,
  getAnnouncementByIdForUser,
  resolveAnnouncementTargetUserIds,
} from "./data-access"
import type { GetAnnouncementsParams, Announcement } from "./types"

/**
 * P1-7: 统一错误处理。
 * - 记录错误堆栈到 console.error 便于生产排查
 * - 上报 trackEvent 用于监控告警
 * - 返回 i18n 化的 message，不向客户端泄露内部错误信息
 */
async function handleActionError(
  e: unknown,
  actionName: string,
  t: (key: string) => string
): Promise<ActionState<never>> {
  console.error(`[announcements] ${actionName} failed:`, e)

  void trackEvent({
    event: "announcement.action_error",
    targetType: "announcement",
    properties: {
      action: actionName,
      errorName: e instanceof Error ? e.constructor.name : "unknown",
      errorMessage: e instanceof Error ? e.message : String(e),
    },
  })

  if (e instanceof PermissionDeniedError) {
    return { success: false, message: t("messages.permissionDenied") }
  }
  if (e instanceof Error && e.message === "FORBIDDEN_ANNOUNCEMENT") {
    return { success: false, message: t("messages.forbidden") }
  }
  return { success: false, message: t("messages.unexpectedError") }
}

/**
 * 发布公告后向目标用户批量发送通知。
 * 通知发送失败不影响公告发布本身，仅记录日志。
 * P1-5: resolveTargetUserIds 已下沉到 data-access.resolveAnnouncementTargetUserIds。
 */
async function notifyAnnouncementPublished(announcement: Announcement): Promise<void> {
  try {
    const targetUserIds = await resolveAnnouncementTargetUserIds(announcement)
    if (targetUserIds.length === 0) return

    const t = await getTranslations("announcements")
    const title = t("notification.publishedTitle", { title: announcement.title })
    const content = t("notification.publishedContent")

    const payloads: NotificationPayload[] = targetUserIds.map((userId) => ({
      userId,
      title,
      content,
      type: "info",
      actionUrl: `/announcements/${announcement.id}`,
      metadata: {
        announcementId: announcement.id,
        announcementType: announcement.type,
      },
    }))

    await sendBatchNotifications(payloads)
  } catch (error) {
    // 通知发送失败不阻塞公告发布流程，仅记录错误
    console.error("Failed to send announcement notifications:", error)
  }
}

export async function createAnnouncementAction(
  prevState: ActionState<string> | null,
  formData: FormData
): Promise<ActionState<string>> {
  const t = await getTranslations("announcements")
  try {
    const ctx = await requirePermission(Permissions.ANNOUNCEMENT_MANAGE)

    const parsed = CreateAnnouncementSchema.safeParse({
      title: formData.get("title"),
      content: formData.get("content"),
      type: formData.get("type") || undefined,
      status: formData.get("status") || undefined,
      targetGradeId: formData.get("targetGradeId") || undefined,
      targetClassId: formData.get("targetClassId") || undefined,
      publishedAt: formData.get("publishedAt") || undefined,
    })

    if (!parsed.success) {
      return {
        success: false,
        message: t("messages.invalidFormData"),
        errors: parsed.error.flatten().fieldErrors,
      }
    }

    const input = parsed.data
    const isPublished = input.status === "published"
    const publishedAt = isPublished
      ? input.publishedAt
        ? new Date(input.publishedAt)
        : new Date()
      : input.publishedAt
        ? new Date(input.publishedAt)
        : null

    const id = await insertAnnouncement({
      id: createId(),
      title: input.title,
      content: input.content,
      type: input.type,
      status: input.status,
      targetGradeId: input.targetGradeId,
      targetClassId: input.targetClassId,
      authorId: ctx.userId,
      publishedAt,
    })

    // 如果创建时直接发布，触发通知（失败不阻塞）
    if (isPublished) {
      const created = await getAnnouncementById(id)
      if (created) {
        await notifyAnnouncementPublished(created)
      }
    }

    revalidatePath("/admin/announcements")
    revalidatePath("/announcements")

    void trackEvent({
      event: isPublished ? "announcement.published" : "announcement.created",
      userId: ctx.userId,
      targetId: id,
      targetType: "announcement",
      properties: { type: input.type, status: input.status },
    })

    return { success: true, message: isPublished ? t("messages.published") : t("messages.created"), data: id }
  } catch (e) {
    return handleActionError(e, "createAnnouncementAction", t)
  }
}

export async function updateAnnouncementAction(
  id: string,
  prevState: ActionState<string> | null,
  formData: FormData
): Promise<ActionState<string>> {
  const t = await getTranslations("announcements")
  try {
    await requirePermission(Permissions.ANNOUNCEMENT_MANAGE)

    const existing = await getAnnouncementById(id)
    if (!existing) return { success: false, message: t("messages.notFound") }

    const parsed = UpdateAnnouncementSchema.safeParse({
      title: formData.get("title"),
      content: formData.get("content"),
      type: formData.get("type") || undefined,
      status: formData.get("status") || undefined,
      targetGradeId: formData.get("targetGradeId") || undefined,
      targetClassId: formData.get("targetClassId") || undefined,
      publishedAt: formData.get("publishedAt") || undefined,
    })

    if (!parsed.success) {
      return {
        success: false,
        message: t("messages.invalidFormData"),
        errors: parsed.error.flatten().fieldErrors,
      }
    }

    const input = parsed.data
    const isPublished = input.status === "published"
    const wasPublished = existing.status === "published"
    const publishedAt = isPublished
      ? existing.publishedAt
        ? new Date(existing.publishedAt)
        : new Date()
      : input.publishedAt
        ? new Date(input.publishedAt)
        : null

    await updateAnnouncementById(id, {
      title: input.title,
      content: input.content,
      type: input.type,
      status: input.status,
      targetGradeId: input.targetGradeId,
      targetClassId: input.targetClassId,
      publishedAt,
      updatedAt: new Date(),
    })

    // 当公告从非发布状态变为发布状态时，触发通知（失败不阻塞）
    if (isPublished && !wasPublished) {
      const updated = await getAnnouncementById(id)
      if (updated) {
        await notifyAnnouncementPublished(updated)
      }
    }

    revalidatePath("/admin/announcements")
    revalidatePath(`/admin/announcements/${id}`)
    revalidatePath("/announcements")

    void trackEvent({
      event: isPublished && !wasPublished ? "announcement.published" : "announcement.updated",
      targetId: id,
      targetType: "announcement",
      properties: { type: input.type, status: input.status, wasPublished },
    })

    return {
      success: true,
      message: isPublished && !wasPublished ? t("messages.published") : t("messages.updated"),
      data: id,
    }
  } catch (e) {
    return handleActionError(e, "updateAnnouncementAction", t)
  }
}

export async function deleteAnnouncementAction(id: string): Promise<ActionState<string>> {
  const t = await getTranslations("announcements")
  try {
    await requirePermission(Permissions.ANNOUNCEMENT_MANAGE)

    const existing = await getAnnouncementById(id)
    if (!existing) return { success: false, message: t("messages.notFound") }

    await deleteAnnouncementById(id)

    revalidatePath("/admin/announcements")
    revalidatePath("/announcements")

    void trackEvent({
      event: "announcement.deleted",
      targetId: id,
      targetType: "announcement",
      properties: { type: existing.type, status: existing.status },
    })

    return { success: true, message: t("messages.deleted") }
  } catch (e) {
    return handleActionError(e, "deleteAnnouncementAction", t)
  }
}

export async function publishAnnouncementAction(id: string): Promise<ActionState<string>> {
  const t = await getTranslations("announcements")
  try {
    await requirePermission(Permissions.ANNOUNCEMENT_MANAGE)

    const existing = await getAnnouncementById(id)
    if (!existing) return { success: false, message: t("messages.notFound") }

    const publishedAt = existing.publishedAt
      ? new Date(existing.publishedAt)
      : new Date()
    await publishAnnouncementById(id, publishedAt)

    // 发布成功后触发通知（失败不阻塞）
    await notifyAnnouncementPublished(existing)

    revalidatePath("/admin/announcements")
    revalidatePath(`/admin/announcements/${id}`)
    revalidatePath("/announcements")

    void trackEvent({
      event: "announcement.published",
      targetId: id,
      targetType: "announcement",
      properties: { type: existing.type },
    })

    return { success: true, message: t("messages.published") }
  } catch (e) {
    return handleActionError(e, "publishAnnouncementAction", t)
  }
}

export async function archiveAnnouncementAction(id: string): Promise<ActionState<string>> {
  const t = await getTranslations("announcements")
  try {
    await requirePermission(Permissions.ANNOUNCEMENT_MANAGE)

    const existing = await getAnnouncementById(id)
    if (!existing) return { success: false, message: t("messages.notFound") }

    await archiveAnnouncementById(id)

    revalidatePath("/admin/announcements")
    revalidatePath(`/admin/announcements/${id}`)
    revalidatePath("/announcements")

    void trackEvent({
      event: "announcement.archived",
      targetId: id,
      targetType: "announcement",
      properties: { type: existing.type },
    })

    return { success: true, message: t("messages.archived") }
  } catch (e) {
    return handleActionError(e, "archiveAnnouncementAction", t)
  }
}

// ---------------------------------------------------------------------------
// V2-P2-13d: 公告置顶 Server Action
// P0-3: 新增资源存在性二次校验
// ---------------------------------------------------------------------------

export async function toggleAnnouncementPinAction(id: string): Promise<ActionState<string>> {
  const t = await getTranslations("announcements")
  try {
    await requirePermission(Permissions.ANNOUNCEMENT_MANAGE)

    // P0-3: 资源存在性校验
    const existing = await getAnnouncementById(id)
    if (!existing) return { success: false, message: t("messages.notFound") }

    await toggleAnnouncementPin(id)
    revalidatePath("/admin/announcements")
    revalidatePath("/announcements")

    void trackEvent({
      event: "announcement.pin_toggled",
      targetId: id,
      targetType: "announcement",
    })

    return { success: true, message: t("messages.pinToggled") }
  } catch (e) {
    return handleActionError(e, "toggleAnnouncementPinAction", t)
  }
}

// ---------------------------------------------------------------------------
// V2-P2-13d: 公告已读标记 Server Action
// P0-3: 新增资源可见性二次校验（防止对草稿/他人班级公告写入已读记录）
// ---------------------------------------------------------------------------

export async function markAnnouncementAsReadAction(announcementId: string): Promise<ActionState<string>> {
  const t = await getTranslations("announcements")
  try {
    const ctx = await requirePermission(Permissions.ANNOUNCEMENT_READ)

    // P0-3: 资源可见性二次校验——必须为已发布且对当前用户受众可见
    const visible = await getAnnouncementByIdForUser(announcementId, ctx.userId, ctx.dataScope)
    if (!visible) {
      // 不可见时抛出特定错误，由 handleActionError 转为 i18n 文案
      throw new Error("FORBIDDEN_ANNOUNCEMENT")
    }

    await markAnnouncementAsRead(announcementId, ctx.userId)

    void trackEvent({
      event: "announcement.marked_read",
      userId: ctx.userId,
      targetId: announcementId,
      targetType: "announcement",
    })

    return { success: true, message: t("messages.markedRead") }
  } catch (e) {
    return handleActionError(e, "markAnnouncementAsReadAction", t)
  }
}

/**
 * 批量获取公告已读状态（用于列表页标记已读/未读）。
 * P1-2: 列表组件消费此 Action 展示已读/未读视觉区分。
 */
export async function getAnnouncementReadStatusAction(
  announcementIds: string[]
): Promise<ActionState<Record<string, boolean>>> {
  const t = await getTranslations("announcements")
  try {
    const ctx = await requirePermission(Permissions.ANNOUNCEMENT_READ)
    const statusMap = await getAnnouncementReadStatusForUser(announcementIds, ctx.userId)
    return { success: true, data: Object.fromEntries(statusMap) }
  } catch (e) {
    return handleActionError(e, "getAnnouncementReadStatusAction", t)
  }
}

export async function getAnnouncementsAction(
  params?: GetAnnouncementsParams
): Promise<ActionState<Announcement[]>> {
  const t = await getTranslations("announcements")
  try {
    await requirePermission(Permissions.ANNOUNCEMENT_READ)
    const data = await getAnnouncements(params)
    return { success: true, data }
  } catch (e) {
    return handleActionError(e, "getAnnouncementsAction", t)
  }
}
