"use server"

import { revalidatePath } from "next/cache"
import { desc, eq } from "drizzle-orm"

import type { ActionState } from "@/shared/types/action-state"
import { requirePermission } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
import { db } from "@/shared/db"
import { loginLogs } from "@/shared/db/schema"
import { logLoginEvent } from "@/shared/lib/login-logger"
import { trackAuthEvent } from "@/shared/lib/track-event"
import { getUserProfile } from "@/modules/users/data-access"

import {
  getSystemSettingsByCategory,
} from "./data-access-system-settings"
import {
  deleteBackupCodes,
  deleteTotpSecret,
  getBackupCodesHashed,
  getTotpSecret,
  getTwoFactorEnabled,
  setBackupCodesHashed,
  setTotpSecret,
  setTwoFactorEnabled,
  setTwoFactorEnabledAt,
} from "./data-access-two-factor"
import {
  buildOtpAuthUrl,
  consumeBackupCode,
  countRemainingBackupCodes,
  generateBackupCodes,
  generateQrCodeDataUrl,
  generateTotpSecret,
  hashBackupCodes,
  verifyBackupCode,
  verifyTotpCode,
} from "./lib/totp"

// --- Types ---

export interface TwoFactorStatus {
  enabled: boolean
  /** 2FA 方式：totp / sms / email，当前仅支持 totp */
  method: "totp" | "sms" | "email"
  /** 启用时间 ISO 字符串 */
  enabledAt: string | null
  /** 剩余备份码数量 */
  backupCodesRemaining: number
}

export interface LoginHistoryItem {
  id: string
  action: "signin" | "signout" | "signup"
  status: "success" | "failure"
  ipAddress: string | null
  userAgent: string | null
  errorMessage: string | null
  createdAt: string
}

export interface SecurityCenterData {
  twoFactor: TwoFactorStatus
  recentLogins: LoginHistoryItem[]
}

/** setup 阶段返回：QR 码 + 密钥（明文，供手动输入） */
export interface TwoFactorSetupData {
  qrCodeDataUrl: string
  secret: string
  otpauthUrl: string
}

// --- Constants ---

const TWO_FACTOR_CATEGORY = "security_policy" as const
const SERVICE_NAME = "Next_Edu"

// --- 2FA Helpers ---

async function getTwoFactorStatus(userId: string): Promise<TwoFactorStatus> {
  // 一次查询 security_policy 分类下所有设置，在内存中过滤当前用户的键（避免 N+1）
  const allSettings = await getSystemSettingsByCategory(TWO_FACTOR_CATEGORY)
  const userPrefix = `:${userId}`

  const enabledRecord = allSettings.find(
    (r) => r.key === `twoFactorEnabled${userPrefix}`,
  )
  const enabledAtRecord = allSettings.find(
    (r) => r.key === `twoFactorEnabledAt${userPrefix}`,
  )
  const backupRecord = allSettings.find(
    (r) => r.key === `totpBackupCodes${userPrefix}`,
  )

  const enabled = enabledRecord?.value === "true"
  const enabledAt = enabledAtRecord?.value || null
  const backupCodesRemaining = backupRecord?.value
    ? countRemainingBackupCodes(backupRecord.value)
    : 0

  return {
    enabled,
    method: "totp",
    enabledAt,
    backupCodesRemaining,
  }
}

// --- Actions ---

/**
 * 获取安全中心数据：2FA 状态 + 最近登录历史
 */
export async function getSecurityCenterAction(): Promise<
  ActionState<SecurityCenterData>
> {
  try {
    const ctx = await requirePermission(Permissions.USER_PROFILE_UPDATE)

    const [twoFactor, recentLoginRows] = await Promise.all([
      getTwoFactorStatus(ctx.userId),
      db
        .select({
          id: loginLogs.id,
          action: loginLogs.action,
          status: loginLogs.status,
          ipAddress: loginLogs.ipAddress,
          userAgent: loginLogs.userAgent,
          errorMessage: loginLogs.errorMessage,
          createdAt: loginLogs.createdAt,
        })
        .from(loginLogs)
        .where(eq(loginLogs.userId, ctx.userId))
        .orderBy(desc(loginLogs.createdAt))
        .limit(10),
    ])

    const recentLogins: LoginHistoryItem[] = recentLoginRows.map((r) => ({
      id: r.id,
      action: r.action,
      status: r.status,
      ipAddress: r.ipAddress ?? null,
      userAgent: r.userAgent ?? null,
      errorMessage: r.errorMessage ?? null,
      createdAt: r.createdAt.toISOString(),
    }))

    return {
      success: true,
      data: { twoFactor, recentLogins },
    }
  } catch (error) {
    const message =
      error instanceof Error ? error.message : "Failed to load security center"
    return { success: false, message }
  }
}

/**
 * 2FA 启用流程 - 第一步：生成密钥与二维码
 *
 * 将密钥写入 DB（但 twoFactorEnabled 仍为 false），等待用户在第二步输入验证码确认。
 * 如果用户已有密钥（之前 setup 但未 verify），会重新生成覆盖。
 */
export async function setupTwoFactorAction(): Promise<
  ActionState<TwoFactorSetupData>
> {
  try {
    const ctx = await requirePermission(Permissions.USER_PROFILE_UPDATE)
    const profile = await getUserProfile(ctx.userId)
    if (!profile) {
      return { success: false, message: "User not found" }
    }

    // 已启用 2FA 的用户不允许重新 setup（需先 disable）
    const enabled = await getTwoFactorEnabled(ctx.userId)
    if (enabled) {
      return { success: false, message: "2FA is already enabled" }
    }

    const secret = generateTotpSecret()
    const otpauthUrl = buildOtpAuthUrl({
      serviceName: SERVICE_NAME,
      accountName: profile.email,
      secret,
    })
    const qrCodeDataUrl = await generateQrCodeDataUrl(otpauthUrl)

    // 暂存密钥（twoFactorEnabled 仍为 false，登录不会要求 2FA）
    await setTotpSecret(ctx.userId, secret)

    return {
      success: true,
      data: { qrCodeDataUrl, secret, otpauthUrl },
    }
  } catch (error) {
    const message =
      error instanceof Error ? error.message : "Failed to setup 2FA"
    return { success: false, message }
  }
}

/**
 * 2FA 启用流程 - 第二步：校验用户输入的一次性码，确认启用
 *
 * 同时生成 10 个备份码（明文只返回一次），哈希后存入 DB。
 */
export async function verifyTwoFactorAction(
  token: string,
): Promise<ActionState<{ backupCodes: string[]; status: TwoFactorStatus }>> {
  try {
    const ctx = await requirePermission(Permissions.USER_PROFILE_UPDATE)

    const secret = await getTotpSecret(ctx.userId)
    if (!secret) {
      return {
        success: false,
        message: "Please setup 2FA first (no secret found)",
      }
    }

    const ok = verifyTotpCode(token, secret)
    if (!ok) {
      return { success: false, message: "Invalid verification code" }
    }

    // 生成备份码
    const backupCodes = generateBackupCodes()
    const hashedJson = await hashBackupCodes(backupCodes)
    await setBackupCodesHashed(ctx.userId, hashedJson)

    // 启用 2FA
    const now = new Date().toISOString()
    await setTwoFactorEnabled(ctx.userId, true)
    await setTwoFactorEnabledAt(ctx.userId, now)

    revalidatePath("/settings")

    const status = await getTwoFactorStatus(ctx.userId)

    // audit-P1-9：2FA 启用埋点（用于 2FA 启用率统计）
    await trackAuthEvent("auth.2fa_enabled", {
      userId: ctx.userId,
    })

    return {
      success: true,
      data: { backupCodes, status },
    }
  } catch (error) {
    const message =
      error instanceof Error ? error.message : "Failed to verify 2FA"
    return { success: false, message }
  }
}

/**
 * 关闭 2FA
 *
 * 需要用户提供一个有效的一次性码或备份码以确认身份。
 * 关闭后清除密钥和备份码。
 */
export async function disableTwoFactorAction(
  token: string,
): Promise<ActionState<TwoFactorStatus>> {
  try {
    const ctx = await requirePermission(Permissions.USER_PROFILE_UPDATE)

    const enabled = await getTwoFactorEnabled(ctx.userId)
    if (!enabled) {
      return { success: false, message: "2FA is not enabled" }
    }

    const secret = await getTotpSecret(ctx.userId)
    const backupHashed = await getBackupCodesHashed(ctx.userId)

    // 先尝试 TOTP 验证，失败再尝试备份码
    let verified = false
    if (secret) {
      verified = verifyTotpCode(token, secret)
    }
    if (!verified && backupHashed) {
      const idx = await verifyBackupCode(token, backupHashed)
      if (idx >= 0) {
        verified = true
        // 消耗该备份码（即使即将删除，也保持数据一致性）
        const nextHashed = await consumeBackupCode(backupHashed, idx)
        await setBackupCodesHashed(ctx.userId, nextHashed)
      }
    }

    if (!verified) {
      return { success: false, message: "Invalid verification code" }
    }

    // 清除所有 2FA 相关数据
    await setTwoFactorEnabled(ctx.userId, false)
    await setTwoFactorEnabledAt(ctx.userId, "")
    await deleteTotpSecret(ctx.userId)
    await deleteBackupCodes(ctx.userId)

    revalidatePath("/settings")

    const status = await getTwoFactorStatus(ctx.userId)

    // audit-P1-9：2FA 禁用埋点（用于 2FA 禁用率告警，可能表明账户安全降级）
    await trackAuthEvent("auth.2fa_disabled", {
      userId: ctx.userId,
    })

    return { success: true, data: status }
  } catch (error) {
    const message =
      error instanceof Error ? error.message : "Failed to disable 2FA"
    return { success: false, message }
  }
}

/**
 * 生成新的备份码（用户用尽后可重新生成）
 *
 * 需要用户提供当前的有效 TOTP 码确认身份。
 */
export async function regenerateBackupCodesAction(
  token: string,
): Promise<ActionState<{ backupCodes: string[]; status: TwoFactorStatus }>> {
  try {
    const ctx = await requirePermission(Permissions.USER_PROFILE_UPDATE)

    const enabled = await getTwoFactorEnabled(ctx.userId)
    if (!enabled) {
      return { success: false, message: "2FA is not enabled" }
    }

    const secret = await getTotpSecret(ctx.userId)
    if (!secret) {
      return { success: false, message: "No TOTP secret found" }
    }

    const ok = verifyTotpCode(token, secret)
    if (!ok) {
      return { success: false, message: "Invalid verification code" }
    }

    const backupCodes = generateBackupCodes()
    const hashedJson = await hashBackupCodes(backupCodes)
    await setBackupCodesHashed(ctx.userId, hashedJson)

    revalidatePath("/settings")

    const status = await getTwoFactorStatus(ctx.userId)
    return {
      success: true,
      data: { backupCodes, status },
    }
  } catch (error) {
    const message =
      error instanceof Error ? error.message : "Failed to regenerate backup codes"
    return { success: false, message }
  }
}

/**
 * 远程登出当前用户的所有其他会话
 *
 * 删除 sessions 表中当前用户的所有记录，并记录一条 signout 日志。
 * 注意：当前系统使用 JWT 策略，sessions 表可能无活跃记录；
 * 此操作主要作为安全处置手段，配合未来的 token 黑名单机制使用。
 *
 * 返回被删除的会话数量。
 */
export async function revokeAllOtherSessionsAction(): Promise<
  ActionState<{ revokedCount: number }>
> {
  try {
    const ctx = await requirePermission(Permissions.USER_PROFILE_UPDATE)
    const profile = await getUserProfile(ctx.userId)
    if (!profile) {
      return { success: false, message: "User not found" }
    }

    // audit-P1-9：JWT 策略下 sessions 表无数据，此 Action 为 no-op。
    // 真正的"远程登出其他设备"需要 JWT 黑名单或短期 token + refresh token，
    // 当前架构不支持。保留此 Action 是为了：
    // 1. 前端 UI 不会因 Action 缺失而报错
    // 2. 记录用户"主动登出其他设备"的意图（用于安全审计）
    const revokedCount = 0

    await logLoginEvent({
      userId: ctx.userId,
      userEmail: profile.email,
      action: "signout",
      status: "success",
      errorMessage:
        "Remote logout requested (JWT-based, no active DB sessions to revoke)",
    })

    revalidatePath("/settings")

    return { success: true, data: { revokedCount } }
  } catch (error) {
    const message =
      error instanceof Error ? error.message : "Failed to revoke sessions"
    return { success: false, message }
  }
}

// --- 登录时 2FA 校验（供 auth.ts 调用，非 Server Action） ---

/**
 * 登录预检：根据邮箱判断该用户是否启用了 2FA。
 *
 * 此函数不验证密码，仅查询 2FA 状态。登录表单在首次提交前可调用此函数，
 * 若返回 required=true 则先展示 2FA 验证码输入框。
 *
 * 为防止邮箱枚举攻击，无论用户是否存在都返回 required=false（不存在则视为未启用）。
 */
export async function preflightTwoFactorAction(
  email: string,
): Promise<{ required: boolean }> {
  try {
    const normalized = email.trim().toLowerCase()
    if (!normalized) return { required: false }

    const { eq } = await import("drizzle-orm")
    const { db } = await import("@/shared/db")
    const { users } = await import("@/shared/db/schema")

    const [user] = await db
      .select({ id: users.id })
      .from(users)
      .where(eq(users.email, normalized))
      .limit(1)

    if (!user) return { required: false }

    const enabled = await getTwoFactorEnabled(user.id)
    return { required: enabled }
  } catch {
    return { required: false }
  }
}

/**
 * 登录时校验 2FA：检查用户是否启用 2FA，并校验提供的一次性码或备份码。
 *
 * 返回值：
 * - { required: true }  — 用户启用了 2FA 但未提供 token，登录流程应要求输入
 * - { required: false, valid: true }  — 未启用 2FA，或已提供有效 token
 * - { required: false, valid: false }  — 启用了 2FA 且提供的 token 无效
 *
 * 此函数不使用 requirePermission（登录时还未建立会话），
 * 由 auth.ts 的 authorize 回调直接调用。
 */
export async function verifyTwoFactorForLogin(params: {
  userId: string
  token?: string
}): Promise<{ required: boolean; valid: boolean }> {
  const { userId, token } = params

  const enabled = await getTwoFactorEnabled(userId)
  if (!enabled) {
    return { required: false, valid: true }
  }

  if (!token) {
    return { required: true, valid: false }
  }

  const secret = await getTotpSecret(userId)
  const backupHashed = await getBackupCodesHashed(userId)

  // 先尝试 TOTP
  if (secret && verifyTotpCode(token, secret)) {
    return { required: true, valid: true }
  }

  // 再尝试备份码
  if (backupHashed) {
    const idx = await verifyBackupCode(token, backupHashed)
    if (idx >= 0) {
      const nextHashed = await consumeBackupCode(backupHashed, idx)
      await setBackupCodesHashed(userId, nextHashed)
      return { required: true, valid: true }
    }
  }

  return { required: true, valid: false }
}
