import { prismaClient, withPrismaTransaction } from './prisma.mjs'

function plusSeconds(seconds) {
  return new Date(Date.now() + Math.max(1, Number(seconds) || 1) * 1000)
}

function staffAccountJson(account) {
  if (!account) return null
  return {
    id: account.id,
    email: account.email,
    phone: account.phone || '',
    displayName: account.displayName || '',
    role: account.role || '',
    status: account.status || '',
    positionTitle: account.positionTitle || '',
    remark: account.remark || '',
    lastLoginAt: account.lastLoginAt,
    createdAt: account.createdAt,
    updatedAt: account.updatedAt,
  }
}

function userSelect() {
  return {
    id: true,
    displayName: true,
    passportNo: true,
    phone: true,
    email: true,
    wechatId: true,
    contactPhone: true,
    organization: true,
    title: true,
    city: true,
    address: true,
    industry: true,
    invoiceTitle: true,
    taxNumber: true,
    invoiceEmail: true,
    membershipLevel: true,
    onboardingCompleted: true,
    status: true,
    lastLoginAt: true,
    createdAt: true,
    updatedAt: true,
  }
}

function userJson(user) {
  if (!user) return null
  return {
    id: user.id,
    displayName: user.displayName || '',
    phone: user.phone || '',
    passportNo: user.passportNo || '',
    wechatId: user.wechatId || '',
    contactPhone: user.contactPhone || '',
    email: user.email || '',
    organization: user.organization || '',
    title: user.title || '',
    city: user.city || '',
    address: user.address || '',
    industry: user.industry || '',
    invoiceTitle: user.invoiceTitle || '',
    taxNumber: user.taxNumber || '',
    invoiceEmail: user.invoiceEmail || '',
    membershipLevel: user.membershipLevel || 'registered',
    onboardingCompleted: Boolean(user.onboardingCompleted),
    status: user.status || '',
    expertApplicationStatus: '',
    expertProfileStatus: '',
    lastLoginAt: user.lastLoginAt,
    createdAt: user.createdAt,
    updatedAt: user.updatedAt,
  }
}

export async function hasValidPhoneCode(phone, code) {
  const prisma = prismaClient()
  const item = await prisma.phoneCode.findFirst({
    where: {
      phone,
      code,
      expiresAt: { gt: new Date() },
    },
    select: { phone: true },
  })
  return Boolean(item)
}

export async function storePhoneCode(phone, code, expiresInSeconds = 600) {
  const prisma = prismaClient()
  return prisma.phoneCode.upsert({
    where: { phone },
    create: {
      phone,
      code,
      expiresAt: plusSeconds(expiresInSeconds),
    },
    update: {
      code,
      expiresAt: plusSeconds(expiresInSeconds),
      createdAt: new Date(),
    },
  })
}

export async function deletePhoneCodeRecord(phone, code) {
  const prisma = prismaClient()
  await prisma.phoneCode.deleteMany({ where: { phone, code } })
}

export async function touchAuthRateLimit(bucket, windowSeconds) {
  const safeWindowSeconds = Math.max(1, Number(windowSeconds) || 60)
  const now = new Date()
  const expiresAt = plusSeconds(safeWindowSeconds)

  return withPrismaTransaction(async (tx) => {
    const current = await tx.authRateLimit.findUnique({ where: { bucket } })
    if (!current) {
      return tx.authRateLimit.upsert({
        where: { bucket },
        create: {
          bucket,
          attempts: 1,
          windowStart: now,
          expiresAt,
        },
        update: {
          attempts: 1,
          windowStart: now,
          expiresAt,
        },
      })
    }

    if (current.expiresAt <= now) {
      return tx.authRateLimit.update({
        where: { bucket },
        data: {
          attempts: 1,
          windowStart: now,
          expiresAt,
        },
      })
    }

    return tx.authRateLimit.update({
      where: { bucket },
      data: { attempts: { increment: 1 } },
    })
  })
}

export async function createStaffSession(accountId, token, ttlSeconds = 12 * 60 * 60) {
  const expiresAt = plusSeconds(ttlSeconds)
  const now = new Date()
  await withPrismaTransaction(async (tx) => {
    await tx.staffSession.create({
      data: {
        token,
        accountId,
        expiresAt,
      },
    })
    await tx.staffAccount.update({
      where: { id: accountId },
      data: {
        lastLoginAt: now,
        updatedAt: now,
      },
    })
  })
}

export async function createUserSessionRecord(userId, token, ttlSeconds = 30 * 24 * 60 * 60) {
  const expiresAt = plusSeconds(ttlSeconds)
  const now = new Date()
  const result = await withPrismaTransaction(async (tx) => {
    const user = await tx.user.update({
      where: { id: userId },
      data: {
        lastLoginAt: now,
        updatedAt: now,
      },
      select: userSelect(),
    }).catch((error) => {
      if (error?.code === 'P2025') return null
      throw error
    })
    if (!user || user.status !== 'active') return null
    await tx.userSession.create({
      data: {
        token,
        userId: user.id,
        expiresAt,
      },
    })
    return user
  })
  return result ? { token, user: userJson(result) } : null
}

export async function findActiveUserBySessionToken(token) {
  const prisma = prismaClient()
  const session = await prisma.userSession.findFirst({
    where: {
      token,
      expiresAt: { gt: new Date() },
    },
    select: { userId: true },
  })
  if (!session) return null
  const user = await prisma.user.findFirst({
    where: {
      id: session.userId,
      status: 'active',
    },
    select: userSelect(),
  })
  return userJson(user)
}

export async function findActiveStaffAccountByPhone(phone) {
  const prisma = prismaClient()
  const account = await prisma.staffAccount.findFirst({
    where: {
      phone: String(phone || '').trim(),
      status: 'active',
    },
  })
  return staffAccountJson(account)
}

export async function findActiveStaffAccountByToken(token) {
  const prisma = prismaClient()
  const session = await prisma.staffSession.findFirst({
    where: {
      token,
      expiresAt: { gt: new Date() },
    },
  })
  if (!session) return null
  const account = await prisma.staffAccount.findFirst({
    where: {
      id: session.accountId,
      status: 'active',
    },
  })
  return staffAccountJson(account)
}

export async function logSmsSendRecord(phone, status, details = {}, options = {}) {
  const prisma = prismaClient()
  try {
    await prisma.smsSendLog.create({
      data: {
        phone,
        scene: details.scene || 'auth',
        provider: details.provider || 'aliyun',
        requestId: details.requestId || '',
        status,
        failureReason: details.failureReason || '',
        clientIp: options.clientIp || options.ip || '',
      },
    })
  } catch (error) {
    if (!['P2021', 'P2022'].includes(error?.code)) throw error
  }
}
