// routes/deals.js
const express = require("express")
const router = express.Router()

const requireAuth = require("../middleware/requireAuth")
const requireNotRestricted = require("../middleware/requireNotRestricted")
const optionalAuth = require("../middleware/optionalAuth")
const { upload } = require("../middleware/upload.middleware")
const { validate } = require("../middleware/validate.middleware")
const { endpoints } = require("@shared/contracts")
const requireApiKey = require("../middleware/requireApiKey")

const userDB = require("../db/user.db")
const {
  getDeals,
  getDealById,
  createDeal,
  getDealEngagement,
  getDealSuggestions,
  getBestWidgetDeals,
} = require("../services/deal.service")
const dealSaveService = require("../services/dealSave.service")
const dealReportService = require("../services/dealReport.service")

const { mapCreateDealRequestToDealCreateData } = require("../adapters/requests/dealCreate.adapter")
const { mapDealToDealDetailResponse } = require("../adapters/responses/dealDetail.adapter")
const { mapDealToDealCardResponse, mapPaginatedDealsToDealCardResponse } = require("../adapters/responses/dealCard.adapter")
const { getClientIp } = require("../utils/requestInfo")
const {
  queueDealImpressions,
  queueDealView,
  queueDealClick,
} = require("../services/redis/dealAnalytics.service")
const { getOrCacheDeal } = require("../services/redis/dealCache.service")
const { enqueueAuditFromRequest, buildAuditMeta } = require("../services/audit.service")
const { AUDIT_ACTIONS } = require("../services/auditActions")

const { deals, users } = endpoints

function parsePage(value) {
  const num = Number(value)
  if (!Number.isInteger(num) || num < 1) return 1
  return num
}

const listQueryValidator = validate(deals.dealsListRequestSchema, "query", "validatedDealListQuery")

const buildViewer = (req) =>
  req.auth ? { userId: req.auth.userId, role: req.auth.role } : null

function createListHandler(preset) {
  return async (req, res) => {
    try {
      const viewer = buildViewer(req)
      const { q, page, limit, hotListId, trendingListId } = req.validatedDealListQuery

      const payload = await getDeals({
        preset,
        q,
        page,
        limit,
        viewer,
        filters: req.query,
        hotListId,
        trendingListId,
      })

      const response = deals.dealsListResponseSchema.parse(
        mapPaginatedDealsToDealCardResponse(payload)
      )
      const dealIds = payload?.results?.map((deal) => deal.id) || []
      queueDealImpressions({
        dealIds,
        userId: req.auth?.userId ?? null,
        ip: getClientIp(req),
      }).catch((err) => console.error("Deal impression queue failed:", err?.message || err))
      res.json(response)
    } catch (err) {
      console.error(err)
      const status = err.statusCode || 500
      res.status(status).json({ error: err.message || "Sunucu hatasi" })
    }
  }
}


// Public deals of a user (viewer optional; self profile => "MY" else "USER_PUBLIC")
router.get(
  "/users/:userName/deals",
  optionalAuth,
  validate(users.userProfileRequestSchema, "params", "validatedUserProfile"),
  listQueryValidator,
  async (req, res) => {
    try {
      const { userName } = req.validatedUserProfile
      const targetUser = await userDB.findUser(
        { username: userName },
        { select: { id: true } }
      )

      if (!targetUser) return res.status(404).json({ error: "Kullanici bulunamadi" })

      const { q, page, limit, hotListId, trendingListId } = req.validatedDealListQuery
      const viewer = buildViewer(req)
      const isSelfProfile = viewer?.userId === targetUser.id
      const preset = isSelfProfile ? "MY" : "USER_PUBLIC"

      const payload = await getDeals({
        preset,
        q,
        page,
        limit,
        targetUserId: targetUser.id,
        viewer,
        filters: req.query,
        hotListId,
        trendingListId,
      })

      const response = deals.dealsListResponseSchema.parse(
        mapPaginatedDealsToDealCardResponse(payload)
      )
      const dealIds = payload?.results?.map((deal) => deal.id) || []
      queueDealImpressions({
        dealIds,
        userId: req.auth?.userId ?? null,
        ip: getClientIp(req),
      }).catch((err) => console.error("Deal impression queue failed:", err?.message || err))
      res.json(response)
    } catch (err) {
      console.error(err)
      const status = err.statusCode || 500
      res.status(status).json({ error: err.message || "Sunucu hatasi" })
    }
  }
)

// My deals (auth required)
router.get(
  "/me/deals",
  requireAuth,
  listQueryValidator,
  createListHandler("MY")
)

router.get("/new", requireApiKey, optionalAuth, listQueryValidator, createListHandler("NEW"))
router.get("/hot", requireApiKey, optionalAuth, listQueryValidator, createListHandler("HOT"))
router.get("/trending", requireApiKey, optionalAuth, listQueryValidator, createListHandler("TRENDING"))

router.get("/search/suggest", optionalAuth, async (req, res) => {
  try {
    const q = String(req.query.q || "").trim()
    const limit = Number(req.query.limit || 8)
    const payload = await getDealSuggestions({ q, limit, viewer: buildViewer(req) })
    const response = mapPaginatedDealsToDealCardResponse({
      page: 1,
      total: payload.results.length,
      totalPages: 1,
      results: payload.results,
    })
    res.json(response.results)
  } catch (err) {
    console.error(err)
    res.status(500).json({ error: "Sunucu hatasi" })
  }
})

// Resolve deal URL (SSR uses api key; user token optional)
router.post("/url", requireApiKey, optionalAuth, async (req, res) => {
  try {
    const dealId = Number(req.body?.dealId)
    if (!Number.isInteger(dealId) || dealId <= 0) {
      return res.status(400).json({ error: "dealId invalid" })
    }

    const deal = await getOrCacheDeal(dealId, { ttlSeconds: 15 * 60 })
    if (!deal) return res.status(404).json({ error: "Deal bulunamadi" })

    if (deal.status === "PENDING" || deal.status === "REJECTED") {
      const isOwner = req.auth?.userId && Number(deal.userId) === Number(req.auth.userId)
      const isMod = req.auth?.role === "MOD" || req.auth?.role === "ADMIN"
      if (!isOwner && !isMod) return res.status(404).json({ error: "Deal bulunamadi" })
    }

    const userId = req.auth?.userId ?? null
    const ip = getClientIp(req)
    queueDealClick({ dealId, userId, ip }).catch((err) =>
      console.error("Deal click queue failed:", err?.message || err)
    )

    res.json({ url: deal.url ?? null })
  } catch (err) {
    console.error(err)
    res.status(500).json({ error: "Sunucu hatasi" })
  }
})

// Report deal (auth required)
router.post("/:id/report", requireAuth, async (req, res) => {
  try {
    const id = Number(req.params.id)
    const reason = req.body?.reason
    const note = req.body?.note
    const result = await dealReportService.createDealReport({
      dealId: id,
      userId: req.auth.userId,
      reason,
      note,
    })
    enqueueAuditFromRequest(
      req,
      AUDIT_ACTIONS.DEAL.REPORT_CREATE,
      buildAuditMeta({
        entityType: "DEAL",
        entityId: id,
        extra: { reason: reason ?? null },
      })
    )
    res.json(result)
  } catch (err) {
    console.error(err)
    const status = err.statusCode || 500
    res.status(status).json({ error: err.message || "Report basarisiz" })
  }
})

// Saved deals (auth required)
router.post("/:id/save", requireAuth, async (req, res) => {
  try {
    const id = Number(req.params.id)
    const result = await dealSaveService.saveDealForUser({
      userId: req.auth.userId,
      dealId: id,
    })
    enqueueAuditFromRequest(
      req,
      AUDIT_ACTIONS.DEAL.SAVE,
      buildAuditMeta({
        entityType: "DEAL",
        entityId: id,
      })
    )
    res.json(result)
  } catch (err) {
    console.error(err)
    const status = err.statusCode || 500
    res.status(status).json({ error: err.message || "Kaydetme basarisiz" })
  }
})

router.delete("/:id/save", requireAuth, async (req, res) => {
  try {
    const id = Number(req.params.id)
    await dealSaveService.removeSavedDealForUser({
      userId: req.auth.userId,
      dealId: id,
    })
    enqueueAuditFromRequest(
      req,
      AUDIT_ACTIONS.DEAL.UNSAVE,
      buildAuditMeta({
        entityType: "DEAL",
        entityId: id,
      })
    )
    res.sendStatus(200)
  } catch (err) {
    console.error(err)
    const status = err.statusCode || 500
    res.status(status).json({ error: err.message || "Silme basarisiz" })
  }
})

router.get("/saved", requireAuth, async (req, res) => {
  try {
    const page = parsePage(req.query.page)
    const payload = await dealSaveService.listSavedDeals({
      userId: req.auth.userId,
      page,
    })
    const response = deals.dealsListResponseSchema.parse(
      mapPaginatedDealsToDealCardResponse(payload)
    )
    res.json(response)
  } catch (err) {
    console.error(err)
    const status = err.statusCode || 500
    res.status(status).json({ error: err.message || "Kaydedilenler alinamadi" })
  }
})

// Best deals widget: hot day/week/year top 5
router.get("/widgets/best", requireApiKey, optionalAuth, async (req, res) => {
  try {
    const viewer = buildViewer(req)
    const limitRaw = Number(req.query.limit ?? 5)
    const limit = Number.isFinite(limitRaw) ? Math.max(1, Math.min(20, limitRaw)) : 5

    const payload = await getBestWidgetDeals({ viewer, limit })
    const hotDay = payload.hotDay.map(mapDealToDealCardResponse)
    const hotWeek = payload.hotWeek.map(mapDealToDealCardResponse)
    const hotMonth = payload.hotMonth.map(mapDealToDealCardResponse)

    const dealIds = [...payload.hotDay, ...payload.hotWeek, ...payload.hotMonth].map((d) => d.id)
    if (dealIds.length) {
      queueDealImpressions({
        dealIds,
        userId: req.auth?.userId ?? null,
        ip: getClientIp(req),
      }).catch((err) => console.error("Deal impression queue failed:", err?.message || err))
    }

    res.json({ hotDay, hotWeek, hotMonth })
  } catch (err) {
    console.error(err)
    const status = err.statusCode || 500
    res.status(status).json({ error: err.message || "Sunucu hatasi" })
  }
})

router.get(
  "/search",
  optionalAuth,
  listQueryValidator,
  async (req, res) => {
    try {
      const { q, page, limit, hotListId, trendingListId } = req.validatedDealListQuery
      if (!q || !q.trim()) {
        return res.json({ results: [], total: 0, totalPages: 0, page })
      }

      const payload = await getDeals({
        preset: "NEW",
        q,
        page,
        limit,
        viewer: buildViewer(req),
        filters: req.query,
        baseWhere: { status: "ACTIVE" },
        hotListId,
        trendingListId,
        useRedisSearch: true,
      })

      const response = deals.dealsListResponseSchema.parse(
        mapPaginatedDealsToDealCardResponse(payload)
      )
      const dealIds = payload?.results?.map((deal) => deal.id) || []
      queueDealImpressions({
        dealIds,
        userId: req.auth?.userId ?? null,
        ip: getClientIp(req),
      }).catch((err) => console.error("Deal impression queue failed:", err?.message || err))
      res.json(response)
    } catch (err) {
      console.error(err)
      const status = err.statusCode || 500
      res.status(status).json({ error: err.message || "Sunucu hatasi" })
    }
  }
)
// TOP deals (daily/weekly/monthly) - viewer optional
router.get("/top", optionalAuth, async (req, res) => {
  try {
    const viewer = buildViewer(req)

    const range = String(req.query.range || "day")
    const limitRaw = Number(req.query.limit ?? 6)
    const limit = Number.isFinite(limitRaw) ? Math.max(1, Math.min(20, limitRaw)) : 6

    let preset = "HOT_DAY"
    if (range === "week") preset = "HOT_WEEK"
    else if (range === "month") preset = "HOT_MONTH"
    else if (range !== "day") return res.status(400).json({ error: "range invalid" })

    const payload = await getDeals({
      preset,
      q: null,
      page: 1,
      limit,
      viewer,
      filters: req.query,
    })

    const response = deals.dealsListResponseSchema.parse(
      mapPaginatedDealsToDealCardResponse(payload)
    )
    const dealIds = payload?.results?.map((deal) => deal.id) || []
    queueDealImpressions({
      dealIds,
      userId: req.auth?.userId ?? null,
      ip: getClientIp(req),
    }).catch((err) => console.error("Deal impression queue failed:", err?.message || err))

    // frontend DealCard[] bekliyor
    res.json(response.results)
  } catch (err) {
    console.error(err)
    const status = err.statusCode || 500
    res.status(status).json({ error: err.message || "Sunucu hatasi" })
  }
})

router.post(
  "/engagement",
  requireAuth,
  validate(deals.dealEngagementRequestSchema, "body", "validatedEngagement"),
  async (req, res) => {
    try {
      const { ids } = req.validatedEngagement
      const viewer = buildViewer(req)
      const engagement = await getDealEngagement(ids, viewer)
      res.json(deals.dealEngagementResponseSchema.parse(engagement))
    } catch (err) {
      const status = err.statusCode || 500
      res.status(status).json({ error: err.message || "Sunucu hatasi" })
    }
  }
)



router.get(
  "/:id",
  optionalAuth,
  validate(deals.dealDetailRequestSchema, "params", "validatedDealId"),
  async (req, res) => {
    try {
      
      const { id } = req.validatedDealId
      const deal = await getDealById(id, buildViewer(req))
      if (!deal) return res.status(404).json({ error: "Deal bulunamadi" })

      queueDealView({
        dealId: deal.id,
        userId: req.auth?.userId ?? null,
        ip: getClientIp(req),
      }).catch((err) => console.error("Deal view queue failed:", err?.message || err))

      const mapped = mapDealToDealDetailResponse(deal)
      res.json(deals.dealDetailResponseSchema.parse(mapped))
    } catch (err) {
      console.error(err)
      res.status(500).json({ error: "Sunucu hatasi" })
    }
  }
)

// Create deal (auth required)
router.post(
  "/",
  requireAuth,
  requireNotRestricted({ checkSuspend: true }),
  upload.array("images", 5),
  validate(deals.dealCreateRequestSchema, "body", "validatedDealPayload"),
  async (req, res) => {
    try {
      const userId = req.auth.userId
      const dealCreateData = mapCreateDealRequestToDealCreateData(
        req.validatedDealPayload,
        userId
      )

      const deal = await createDeal(dealCreateData, req.files || [])
      const mapped = mapDealToDealDetailResponse(deal)
      enqueueAuditFromRequest(
        req,
        AUDIT_ACTIONS.DEAL.CREATE,
        buildAuditMeta({
          entityType: "DEAL",
          entityId: deal.id,
          after: { title: deal.title },
        })
      )
      res.json(deals.dealCreateResponseSchema.parse(mapped))
    } catch (err) {
      console.error(err)
      res.status(500).json({ error: "Sunucu hatasi" })
    }
  }
)

module.exports = router

