// db/refreshToken.db.js
const prisma = require("./client")

function toDate(x) {
  if (!x) return null
  if (x instanceof Date) return x
  const d = new Date(x)
  return Number.isNaN(d.getTime()) ? null : d
}

async function createRefreshToken(userId, input = {}) {
  const data = {
    userId: Number(userId),
    tokenHash: input.tokenHash,          // required
    familyId: input.familyId,            // required
    jti: input.jti,                      // required
    expiresAt: toDate(input.expiresAt) || new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),
    createdByIp: input.createdByIp ?? null,
    userAgent: input.userAgent ?? null,
  }

  return prisma.refreshToken.create({ data })
}

async function findRefreshTokenByHash(tokenHash, options = {}) {
  return prisma.refreshToken.findUnique({
    where: { tokenHash },
    select: options.select || undefined,
    include: options.include || undefined,
  })
}

async function revokeRefreshTokenById(id, meta = {}) {
  return prisma.refreshToken.update({
    where: { id },
    data: {
      revokedAt: meta.revokedAt ?? new Date(),
      // optional audit
      createdByIp: meta.createdByIp ?? undefined,
      userAgent: meta.userAgent ?? undefined,
    },
  })
}

async function revokeRefreshTokenByHash(tokenHash, meta = {}) {
  return prisma.refreshToken.update({
    where: { tokenHash },
    data: {
      revokedAt: meta.revokedAt ?? new Date(),
      createdByIp: meta.createdByIp ?? undefined,
      userAgent: meta.userAgent ?? undefined,
    },
  })
}

// Rotation: eski token -> revoked + replacedById set, yeni token create
async function rotateRefreshToken({ oldId, newToken = {}, meta = {} }) {
  return prisma.$transaction(async (tx) => {
    const created = await tx.refreshToken.create({
      data: {
        userId: Number(newToken.userId),
        tokenHash: newToken.tokenHash,
        familyId: newToken.familyId,
        jti: newToken.jti,
        expiresAt: toDate(newToken.expiresAt),
        createdByIp: meta.createdByIp ?? null,
        userAgent: meta.userAgent ?? null,
      },
    })

    const revoked = await tx.refreshToken.update({
      where: { id: oldId },
      data: {
        revokedAt: meta.revokedAt ?? new Date(),
        replacedById: created.id,
      },
    })

    return { created, revoked }
  })
}

// Reuse tespiti / güvenlik: aynı ailedeki tüm tokenları revoke et
async function revokeRefreshTokenFamily(familyId) {
  return prisma.refreshToken.updateMany({
    where: { familyId, revokedAt: null },
    data: { revokedAt: new Date() },
  })
}

async function revokeAllUserRefreshTokens(userId) {
  return prisma.refreshToken.updateMany({
    where: { userId: Number(userId), revokedAt: null },
    data: { revokedAt: new Date() },
  })
}

module.exports = {
  createRefreshToken,
  findRefreshTokenByHash,
  revokeRefreshTokenById,
  revokeRefreshTokenByHash,
  rotateRefreshToken,
  revokeRefreshTokenFamily,
  revokeAllUserRefreshTokens,
}
