const bcrypt = require("bcryptjs")
const userDb = require("../db/user.db")
const notificationDb = require("../db/notification.db")
const refreshTokenDb = require("../db/refreshToken.db")
const { queueNotificationReadAll } = require("./redis/dbSync.service")

function assertPositiveInt(v, name = "id") {
  const n = Number(v)
  if (!Number.isInteger(n) || n <= 0) throw new Error(`Geçersiz ${name}.`)
  return n
}

async function updateAvatarUrl(userId, url) {
  const id = assertPositiveInt(userId, "userId")
  if (!url || typeof url !== "string" || !url.trim())
    throw new Error("Geçersiz URL.")

  const select = { id: true, username: true, avatarUrl: true }
  return userDb.updateUser({ id }, { avatarUrl: url.trim() }, { select })
}

async function getUserProfile(userId) {
  const id = assertPositiveInt(userId, "userId")
  const select = {
    id: true,
    username: true,
    avatarUrl: true,
    createdAt: true,
    userBadges: {
      orderBy: { earnedAt: "desc" },
      select: {
        earnedAt: true,
        badge: { select: { id: true, name: true, iconUrl: true, description: true } },
      },
    },
    notifications: {
      orderBy: { createdAt: "desc" },
      take: 3,
      select: {
        id: true,
        message: true,
        type: true,
        createdAt: true,
        readAt: true,
      },
    },
  }
  const user = await userDb.findUser({ id }, { select })
  if (!user) return user

  const formatDate = (value) => (value instanceof Date ? value.toISOString() : value ?? null)

  const notifications = Array.isArray(user.notifications)
    ? user.notifications.map((n) => ({
        ...n,
        createdAt: formatDate(n.createdAt),
        readAt: formatDate(n.readAt),
        unread: n.readAt == null,
      }))
    : []

  const badges = Array.isArray(user.userBadges)
    ? user.userBadges.map((item) => ({
        badge: item.badge
          ? {
              id: item.badge.id,
              name: item.badge.name,
              iconUrl: item.badge.iconUrl ?? null,
              description: item.badge.description ?? null,
            }
          : null,
        earnedAt: formatDate(item.earnedAt),
      }))
    : []

  return {
    id: user.id,
    username: user.username,
    avatarUrl: user.avatarUrl ?? null,
    createdAt: formatDate(user.createdAt),
    notifications,
    badges,
  }
}

module.exports = {
  updateAvatarUrl,
  getUserProfile,
  markAllNotificationsRead,
  getUserNotificationsPage,
  changePassword,
}

async function markAllNotificationsRead(userId) {
  const id = assertPositiveInt(userId, "userId")
  const readAt = new Date().toISOString()
  await queueNotificationReadAll({ userId: id, readAt })
  return { queued: true, readAt }
}

async function getUserNotificationsPage(userId, page = 1, limit = 10) {
  const id = assertPositiveInt(userId, "userId")
  const pageNumber = assertPositiveInt(page, "page")
  const take = assertPositiveInt(limit, "limit")
  const skip = (pageNumber - 1) * take

  const [total, notifications] = await Promise.all([
    notificationDb.countNotifications({ userId: id }),
    notificationDb.findNotifications(
      { userId: id },
      {
        orderBy: { createdAt: "desc" },
        skip,
        take,
        select: {
          id: true,
          message: true,
          type: true,
          createdAt: true,
          readAt: true,
        },
      }
    ),
  ])

  const formatDate = (value) => (value instanceof Date ? value.toISOString() : value ?? null)
  const results = Array.isArray(notifications)
    ? notifications.map((n) => ({
        ...n,
        createdAt: formatDate(n.createdAt),
        readAt: formatDate(n.readAt),
        unread: n.readAt == null,
      }))
    : []

  const totalPages = Math.ceil(total / take)

  return {
    page: pageNumber,
    total,
    totalPages,
    results,
  }
}

async function changePassword(userId, { currentPassword, newPassword }) {
  const id = assertPositiveInt(userId, "userId")
  if (!currentPassword || typeof currentPassword !== "string")
    throw new Error("Mevcut şifre gerekli.")
  if (!newPassword || typeof newPassword !== "string")
    throw new Error("Yeni şifre gerekli.")

  const user = await userDb.findUser(
    { id },
    { select: { id: true, passwordHash: true } }
  )
  if (!user) throw new Error("Kullanıcı bulunamadı.")

  const isMatch = await bcrypt.compare(currentPassword, user.passwordHash)
  if (!isMatch) throw new Error("Mevcut şifre hatalı.")

  const passwordHash = await bcrypt.hash(newPassword, 10)
  await userDb.updateUser({ id }, { passwordHash })
  await refreshTokenDb.revokeAllUserRefreshTokens(id)

  return { message: "Şifre güncellendi." }
}
