const dealService = require("./deal.service")
const dealAiReviewDB = require("../db/dealAiReview.db")
const categoryDB = require("../db/category.db")
const { findCategoryById, listCategories } = categoryDB
const { findSeller } = require("../db/seller.db")
const {
  getOrCacheDealForModeration,
  updateDealInRedis,
} = require("./redis/dealCache.service")
const { queueDealUpdate, queueNotificationCreate } = require("./redis/dbSync.service")
const { publishNotification } = require("./redis/notificationPubsub.service")
const { getSellerById } = require("./redis/sellerCache.service")
const { attachTagsToDeal, normalizeTags } = require("./tag.service")

function normalizeDealForModResponse(deal) {
  if (!deal) return deal
  const images = Array.isArray(deal.images)
    ? deal.images.map((img, idx) => ({
        id: img.id ?? 0,
        imageUrl: img.imageUrl,
        order: img.order ?? idx,
      }))
    : []

  return {
    ...deal,
    images,
    myVote: deal.myVote ?? 0,
    _count: { comments: Number(deal.commentCount ?? 0) },
  }
}

async function enrichDealSeller(deal) {
  if (!deal || deal.seller || !deal.sellerId) return deal
  const seller = await getSellerById(Number(deal.sellerId))
  if (!seller) return deal
  return { ...deal, seller }
}

async function getPendingDeals({ page = 1, limit = 10, filters = {}, viewer = null } = {}) {
  return dealService.getDeals({
    preset: "RAW",
    q: filters?.q,
    page,
    limit,
    viewer,
    scope: "MOD",
    baseWhere: { status: "PENDING" },
    filters,
    useRedisSearch: true,
  })
}

async function updateDealStatus(dealId, nextStatus) {
  const id = Number(dealId)
  if (!Number.isInteger(id) || id <= 0) {
    const err = new Error("INVALID_DEAL_ID")
    err.statusCode = 400
    throw err
  }

  const { deal } = await getOrCacheDealForModeration(id)
  if (!deal) {
    const err = new Error("DEAL_NOT_FOUND")
    err.statusCode = 404
    throw err
  }

  if (deal.status === nextStatus) return { id: deal.id, status: deal.status }

  const updatedAt = new Date()
  await updateDealInRedis(id, { status: nextStatus }, { updatedAt })
  queueDealUpdate({
    dealId: id,
    data: { status: nextStatus },
    updatedAt: updatedAt.toISOString(),
  }).catch((err) => console.error("DB sync deal status update failed:", err?.message || err))

  return { id: deal.id, status: nextStatus }
}

async function approveDeal(dealId) {
  const id = Number(dealId)
  if (!Number.isInteger(id) || id <= 0) {
    const err = new Error("INVALID_DEAL_ID")
    err.statusCode = 400
    throw err
  }

  const { deal } = await getOrCacheDealForModeration(id)
  if (!deal) {
    const err = new Error("DEAL_NOT_FOUND")
    err.statusCode = 404
    throw err
  }

  const aiReviewFromDb = await dealAiReviewDB.findDealAiReviewByDealId(id, {
    select: { bestCategoryId: true, tags: true },
  })

  let categoryId = Number(deal.categoryId || 0)
  if (!categoryId) {
    const aiCategoryId = Number(deal.aiReview?.bestCategoryId || 0)
    if (aiCategoryId > 0) {
      categoryId = aiCategoryId
    } else {
      const fallbackId = aiReviewFromDb?.bestCategoryId ?? 0
      if (!Number.isInteger(fallbackId) || fallbackId <= 0) {
        const err = new Error("CATEGORY_REQUIRED")
        err.statusCode = 400
        throw err
      }
      categoryId = fallbackId
    }
  }

  const aiTags = Array.isArray(deal.aiReview?.tags)
    ? deal.aiReview.tags
    : Array.isArray(aiReviewFromDb?.tags)
      ? aiReviewFromDb.tags
      : []
  const normalizedTags = normalizeTags(aiTags)

  if (deal.status === "ACTIVE" && categoryId === Number(deal.categoryId || 0) && !normalizedTags.length) {
    return { id: deal.id, status: deal.status }
  }

  const tagResult = normalizedTags.length
    ? await attachTagsToDeal(id, normalizedTags)
    : { tags: [] }

  const updatedAt = new Date()
  const redisPatch = { status: "ACTIVE", categoryId }
  if (tagResult.tags?.length) {
    redisPatch.tags = tagResult.tags
  }
  await updateDealInRedis(id, redisPatch, { updatedAt })
  queueDealUpdate({
    dealId: id,
    data: { status: "ACTIVE", categoryId },
    updatedAt: updatedAt.toISOString(),
  }).catch((err) => console.error("DB sync deal approve failed:", err?.message || err))

  if (Number.isInteger(Number(deal.userId)) && Number(deal.userId) > 0) {
    const payload = {
      userId: Number(deal.userId),
      message: "Fırsatın onaylandı!",
      type: "MODERATION",
      createdAt: updatedAt.toISOString(),
    }
    queueNotificationCreate(payload).catch((err) =>
      console.error("DB sync approval notification failed:", err?.message || err)
    )
    publishNotification(payload).catch((err) =>
      console.error("Approval notification publish failed:", err?.message || err)
    )
  }

  return { id: deal.id, status: "ACTIVE" }
}

async function rejectDeal(dealId) {
  return updateDealStatus(dealId, "REJECTED")
}

async function expireDeal(dealId) {
  return updateDealStatus(dealId, "EXPIRED")
}

async function unexpireDeal(dealId) {
  return updateDealStatus(dealId, "ACTIVE")
}

async function getDealDetailForMod(dealId, viewer = null) {
  const deal = await dealService.getDealById(dealId, viewer)
  if (!deal) {
    const err = new Error("DEAL_NOT_FOUND")
    err.statusCode = 404
    throw err
  }

  const aiReview = await dealAiReviewDB.findDealAiReviewByDealId(Number(dealId), {
    select: {
      dealId: true,
      bestCategoryId: true,
      tags: true,
      needsReview: true,
      hasIssue: true,
      issueType: true,
      issueReason: true,
      createdAt: true,
    },
  })

  const categoryBreadcrumb = aiReview
    ? await categoryDB.getCategoryBreadcrumb(aiReview.bestCategoryId, { includeUndefined: false })
    : []

  return {
    deal,
    aiReview: aiReview ? { ...aiReview, categoryBreadcrumb } : null,
  }
}

async function updateDealForMod(dealId, input = {}, viewer = null) {
  const id = Number(dealId)
  if (!Number.isInteger(id) || id <= 0) {
    const err = new Error("INVALID_DEAL_ID")
    err.statusCode = 400
    throw err
  }

  const { deal: existing } = await getOrCacheDealForModeration(id)
  if (!existing) {
    const err = new Error("DEAL_NOT_FOUND")
    err.statusCode = 404
    throw err
  }

  if (input.sellerId !== undefined && input.customSeller !== undefined) {
    const err = new Error("SELLER_CONFLICT")
    err.statusCode = 400
    throw err
  }

  const data = {}

  if (input.title !== undefined) data.title = input.title
  if (input.description !== undefined) data.description = input.description ?? null
  if (input.url !== undefined) data.url = input.url ?? null
  if (input.price !== undefined) data.price = input.price ?? null
  if (input.originalPrice !== undefined) data.originalPrice = input.originalPrice ?? null
  if (input.shippingPrice !== undefined) data.shippingPrice = input.shippingPrice ?? null
  if (input.couponCode !== undefined) data.couponCode = input.couponCode ?? null
  if (input.location !== undefined) data.location = input.location ?? null
  if (input.discountValue !== undefined) data.discountValue = input.discountValue ?? null
  if (input.discountType !== undefined) {
    const normalized =
      typeof input.discountType === "string" ? input.discountType.toUpperCase() : null
    if (normalized && !["PERCENT", "AMOUNT"].includes(normalized)) {
      const err = new Error("INVALID_DISCOUNT_TYPE")
      err.statusCode = 400
      throw err
    }
    data.discountType = normalized
  }
  if (input.saleType !== undefined) {
    const normalized = typeof input.saleType === "string" ? input.saleType.toUpperCase() : null
    if (normalized && !["ONLINE", "OFFLINE", "CODE"].includes(normalized)) {
      const err = new Error("INVALID_SALE_TYPE")
      err.statusCode = 400
      throw err
    }
    data.saletype = normalized
  }

  if (input.sellerId !== undefined) {
    const sellerId = Number(input.sellerId)
    if (!Number.isInteger(sellerId) || sellerId <= 0) {
      const err = new Error("INVALID_SELLER_ID")
      err.statusCode = 400
      throw err
    }
    const seller = await findSeller({ id: sellerId }, { select: { id: true } })
    if (!seller) {
      const err = new Error("SELLER_NOT_FOUND")
      err.statusCode = 404
      throw err
    }
    data.sellerId = sellerId
    data.customSeller = null
  }

  if (input.customSeller !== undefined) {
    const normalized =
      typeof input.customSeller === "string" ? input.customSeller.trim() : null
    data.customSeller = normalized || null
    if (data.customSeller) data.sellerId = null
  }

  if (input.categoryId !== undefined) {
    const categoryId = Number(input.categoryId)
    if (!Number.isInteger(categoryId) || categoryId < 0) {
      const err = new Error("INVALID_CATEGORY_ID")
      err.statusCode = 400
      throw err
    }
    if (categoryId > 0) {
      const category = await findCategoryById(categoryId, { select: { id: true } })
      if (!category) {
        const err = new Error("CATEGORY_NOT_FOUND")
        err.statusCode = 404
        throw err
      }
    }
    data.categoryId = categoryId
  }

  if (!Object.keys(data).length) {
    const enriched = await enrichDealSeller(existing)
    return normalizeDealForModResponse(enriched)
  }

  const updatedAt = new Date()
  const updated = await updateDealInRedis(id, data, { updatedAt })
  queueDealUpdate({
    dealId: id,
    data,
    updatedAt: updatedAt.toISOString(),
  }).catch((err) => console.error("DB sync deal update failed:", err?.message || err))

  const normalized = updated || existing
  const enriched = await enrichDealSeller(normalized)
  return normalizeDealForModResponse(enriched)
}

async function listAllCategories() {
  return listCategories({
    select: { id: true, name: true, parentId: true },
    orderBy: { id: "asc" },
  })
}

module.exports = {
  getPendingDeals,
  approveDeal,
  rejectDeal,
  expireDeal,
  unexpireDeal,
  getDealDetailForMod,
  updateDealForMod,
  listAllCategories,
}
