const { randomUUID } = require("crypto")
const { getRedisClient } = require("./client")
const dealAnalyticsDb = require("../../db/dealAnalytics.db")
const { ensureMinDealTtl } = require("./dealCache.service")
const { DEAL_ANALYTICS_TOTAL_HASH_KEY } = require("./dbSync.service")
const DEAL_ANALYTICS_TOTAL_PREFIX = "deals:analytics:total:"

function createRedisClient() {
  return getRedisClient()
}

function getTotalKey(dealId) {
  return `${DEAL_ANALYTICS_TOTAL_PREFIX}${dealId}`
}

function normalizeIds(ids = []) {
  return Array.from(
    new Set(
      (Array.isArray(ids) ? ids : [])
        .map((id) => Number(id))
        .filter((id) => Number.isInteger(id) && id > 0)
    )
  )
}

function isValidEventType(type) {
  const normalized = String(type || "").toUpperCase()
  return ["IMPRESSION", "VIEW", "CLICK"].includes(normalized)
}

function aggregateEventIncrements(events = []) {
  const byDeal = new Map()
  for (const event of events) {
    const dealId = Number(event.dealId)
    if (!Number.isInteger(dealId) || dealId <= 0) continue
    const type = String(event.type || "").toUpperCase()
    const entry = byDeal.get(dealId) || { dealId, impressions: 0, views: 0, clicks: 0 }
    if (type === "IMPRESSION") entry.impressions += 1
    else if (type === "VIEW") entry.views += 1
    else if (type === "CLICK") entry.clicks += 1
    byDeal.set(dealId, entry)
  }
  return Array.from(byDeal.values())
}

async function seedDealAnalyticsTotals({ dealIds = [] } = {}) {
  const ids = normalizeIds(dealIds)
  if (!ids.length) return 0

  await dealAnalyticsDb.ensureTotalsForDealIds(ids)
  const totals = await dealAnalyticsDb.getTotalsByDealIds(ids)
  const totalsById = new Map(totals.map((t) => [t.dealId, t]))

  const redis = createRedisClient()
  try {
    const pipeline = redis.pipeline()
    ids.forEach((id) => {
      const total = totalsById.get(id) || { impressions: 0, views: 0, clicks: 0 }
      pipeline.hset(
        getTotalKey(id),
        "impressions",
        String(total.impressions || 0),
        "views",
        String(total.views || 0),
        "clicks",
        String(total.clicks || 0)
      )
    })
    await pipeline.exec()
    return ids.length
  } finally {}
}

async function initDealAnalyticsTotal(dealId) {
  const id = Number(dealId)
  if (!Number.isInteger(id) || id <= 0) return 0
  await dealAnalyticsDb.ensureTotalsForDealIds([id])
  await seedDealAnalyticsTotals({ dealIds: [id] })
  return 1
}

async function queueDealEvents(events = []) {
  const valid = (Array.isArray(events) ? events : []).filter(
    (e) =>
      e &&
      Number.isInteger(Number(e.dealId)) &&
      (e.userId || e.ip) &&
      isValidEventType(e.type)
  )
  if (!valid.length) return 0

  const increments = aggregateEventIncrements(valid)
  if (!increments.length) return 0

  const redis = createRedisClient()
  try {
    await incrementDealAnalyticsTotalsInRedis(increments)
    const pipeline = redis.pipeline()
    increments.forEach((entry) => {
      const field = `dealTotals:${entry.dealId}:${randomUUID()}`
      pipeline.hset(DEAL_ANALYTICS_TOTAL_HASH_KEY, field, JSON.stringify(entry))
    })
    await pipeline.exec()
    return valid.length
  } catch {
    try {
      await dealAnalyticsDb.applyDealTotalsBatch(increments)
      return valid.length
    } catch {
      return 0
    }
  } finally {}
}

async function queueDealImpressions({ dealIds = [], userId = null, ip = null } = {}) {
  if (!userId && !ip) return 0
  const ids = normalizeIds(dealIds)
  if (!ids.length) return 0
  const events = ids.map((dealId) => ({
    dealId,
    type: "IMPRESSION",
    userId,
    ip,
  }))
  await Promise.all(ids.map((id) => ensureMinDealTtl(id, { minSeconds: 15 * 60 })))
  return queueDealEvents(events)
}

async function queueDealView({ dealId, userId = null, ip = null } = {}) {
  if (!userId && !ip) return 0
  const id = Number(dealId)
  if (!Number.isInteger(id) || id <= 0) return 0
  await ensureMinDealTtl(id, { minSeconds: 15 * 60 })
  return queueDealEvents([
    {
      dealId: id,
      type: "VIEW",
      userId,
      ip,
    },
  ])
}

async function queueDealClick({ dealId, userId = null, ip = null } = {}) {
  if (!userId && !ip) return 0
  const id = Number(dealId)
  if (!Number.isInteger(id) || id <= 0) return 0
  await ensureMinDealTtl(id, { minSeconds: 15 * 60 })
  return queueDealEvents([
    {
      dealId: id,
      type: "CLICK",
      userId,
      ip,
    },
  ])
}

async function incrementDealAnalyticsTotalsInRedis(increments = []) {
  const data = (Array.isArray(increments) ? increments : []).filter(
    (item) => item && Number.isInteger(Number(item.dealId))
  )
  if (!data.length) return 0
  const redis = createRedisClient()
  try {
    const pipeline = redis.pipeline()
    data.forEach((item) => {
      const key = getTotalKey(item.dealId)
      if (item.impressions) pipeline.hincrby(key, "impressions", Number(item.impressions))
      if (item.views) pipeline.hincrby(key, "views", Number(item.views))
      if (item.clicks) pipeline.hincrby(key, "clicks", Number(item.clicks))
    })
    await pipeline.exec()
    return data.length
  } catch {
    return 0
  } finally {}
}

module.exports = {
  seedDealAnalyticsTotals,
  initDealAnalyticsTotal,
  queueDealImpressions,
  queueDealView,
  queueDealClick,
  incrementDealAnalyticsTotalsInRedis,
  DEAL_ANALYTICS_TOTAL_HASH_KEY,
}
