const prisma = require("../db/client")
const { slugify } = require("../utils/slugify")

function normalizeTags(tags = []) {
  const arr = Array.isArray(tags) ? tags : []
  const map = new Map()

  arr.forEach((raw) => {
    let name = ""
    if (typeof raw === "string") {
      name = raw
    } else if (raw && typeof raw === "object") {
      if (typeof raw.name === "string") name = raw.name
      else if (typeof raw.title === "string") name = raw.title
      else if (typeof raw.slug === "string") name = raw.slug
      else if (typeof raw.label === "string") name = raw.label
      else if (typeof raw.text === "string") name = raw.text
    }

    const trimmed = String(name || "").trim()
    if (!trimmed) return
    const slug = slugify(trimmed)
    if (!slug) return
    if (!map.has(slug)) {
      map.set(slug, { slug, name: trimmed })
    }
  })

  return Array.from(map.values()).slice(0, 5)
}

async function attachTagsToDeal(dealId, tags = []) {
  const id = Number(dealId)
  if (!Number.isInteger(id) || id <= 0) throw new Error("INVALID_DEAL_ID")

  const normalized = normalizeTags(tags)
  if (!normalized.length) return { tags: [], created: 0 }

  return prisma.$transaction(async (tx) => {
    const upserted = await Promise.all(
      normalized.map((t) =>
        tx.tag.upsert({
          where: { slug: t.slug },
          update: {},
          create: { slug: t.slug, name: t.name },
        })
      )
    )

    const tagIds = upserted.map((t) => t.id)
    const existing = await tx.dealTag.findMany({
      where: { dealId: id, tagId: { in: tagIds } },
      select: { tagId: true },
    })
    const existingIds = new Set(existing.map((e) => e.tagId))
    const toCreate = upserted.filter((t) => !existingIds.has(t.id))

    if (toCreate.length) {
      await tx.dealTag.createMany({
        data: toCreate.map((t) => ({ dealId: id, tagId: t.id })),
        skipDuplicates: true,
      })
      await tx.tag.updateMany({
        where: { id: { in: toCreate.map((t) => t.id) } },
        data: { usageCount: { increment: 1 } },
      })
    }

    const tagsForDeal = await tx.dealTag.findMany({
      where: { dealId: id },
      select: { tag: { select: { id: true, slug: true, name: true } } },
    })

    return {
      tags: tagsForDeal.map((entry) => entry.tag),
      created: toCreate.length,
    }
  })
}

async function removeTagsFromDeal(dealId, tags = []) {
  const id = Number(dealId)
  if (!Number.isInteger(id) || id <= 0) throw new Error("INVALID_DEAL_ID")

  const normalized = normalizeTags(tags)
  if (!normalized.length) return { tags: [], removed: 0 }
  const slugs = normalized.map((t) => t.slug)

  return prisma.$transaction(async (tx) => {
    const tagRows = await tx.tag.findMany({
      where: { slug: { in: slugs } },
      select: { id: true },
    })
    if (!tagRows.length) return { tags: [], removed: 0 }
    const tagIds = tagRows.map((t) => t.id)

    const existing = await tx.dealTag.findMany({
      where: { dealId: id, tagId: { in: tagIds } },
      select: { tagId: true },
    })
    const existingIds = new Set(existing.map((e) => e.tagId))
    const toRemove = tagIds.filter((tagId) => existingIds.has(tagId))
    if (toRemove.length) {
      await tx.dealTag.deleteMany({ where: { dealId: id, tagId: { in: toRemove } } })
      await tx.tag.updateMany({
        where: { id: { in: toRemove }, usageCount: { gt: 0 } },
        data: { usageCount: { decrement: 1 } },
      })
    }

    const tagsForDeal = await tx.dealTag.findMany({
      where: { dealId: id },
      select: { tag: { select: { id: true, slug: true, name: true } } },
    })

    return {
      tags: tagsForDeal.map((entry) => entry.tag),
      removed: toRemove.length,
    }
  })
}

async function replaceTagsForDeal(dealId, tags = []) {
  const id = Number(dealId)
  if (!Number.isInteger(id) || id <= 0) throw new Error("INVALID_DEAL_ID")

  const normalized = normalizeTags(tags)

  return prisma.$transaction(async (tx) => {
    const existing = await tx.dealTag.findMany({
      where: { dealId: id },
      select: { tagId: true },
    })
    const existingIds = new Set(existing.map((e) => e.tagId))

    let desiredIds = []
    if (normalized.length) {
      const upserted = await Promise.all(
        normalized.map((t) =>
          tx.tag.upsert({
            where: { slug: t.slug },
            update: {},
            create: { slug: t.slug, name: t.name },
          })
        )
      )
      desiredIds = upserted.map((t) => t.id)
    }

    const desiredSet = new Set(desiredIds)
    const toRemove = Array.from(existingIds).filter((tagId) => !desiredSet.has(tagId))
    const toAdd = desiredIds.filter((tagId) => !existingIds.has(tagId))

    if (toRemove.length) {
      await tx.dealTag.deleteMany({ where: { dealId: id, tagId: { in: toRemove } } })
      await tx.tag.updateMany({
        where: { id: { in: toRemove }, usageCount: { gt: 0 } },
        data: { usageCount: { decrement: 1 } },
      })
    }

    if (toAdd.length) {
      await tx.dealTag.createMany({
        data: toAdd.map((tagId) => ({ dealId: id, tagId })),
        skipDuplicates: true,
      })
      await tx.tag.updateMany({
        where: { id: { in: toAdd } },
        data: { usageCount: { increment: 1 } },
      })
    }

    const tagsForDeal = await tx.dealTag.findMany({
      where: { dealId: id },
      select: { tag: { select: { id: true, slug: true, name: true } } },
    })

    return {
      tags: tagsForDeal.map((entry) => entry.tag),
      added: toAdd.length,
      removed: toRemove.length,
    }
  })
}

module.exports = {
  attachTagsToDeal,
  normalizeTags,
  removeTagsFromDeal,
  replaceTagsForDeal,
}
