/**
 * Parse a date value (string, Date, or timestamp) into a Date object.
 * @param {string|Date|number} dateValue
 * @returns {Date|null}
 */
function parseDate(dateValue) {
  if (!dateValue) return null

  if (dateValue instanceof Date) {
    return isNaN(dateValue.getTime()) ? null : dateValue
  }

  const date = new Date(dateValue)
  return !isNaN(date.getTime()) ? date : null
}

/**
 * @param {string|Date} dateString
 * @returns {boolean}
 */
function isFutureDate(dateString) {
  const date = parseDate(dateString)
  if (!date) return false

  const now = new Date()
  const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
  const scheduleDate = new Date(date.getFullYear(), date.getMonth(), date.getDate())
  return scheduleDate > today
}

/**
 * @param {string|Date} dateString
 * @returns {boolean}
 */
function isTodayOrPast(dateString) {
  const date = parseDate(dateString)
  if (!date) return false

  const now = new Date()
  const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
  const scheduleDate = new Date(date.getFullYear(), date.getMonth(), date.getDate())
  return scheduleDate <= today
}

/**
 * Check if a banner has a schedule matching the given date range (future schedule within bounds).
 * @param {object} banner
 * @param {string|undefined} startDate
 * @param {string|undefined} endDate
 * @returns {boolean}
 */
function bannerMatchesSchedule(banner, startDate, endDate) {
  if (!banner.schedules || !Array.isArray(banner.schedules)) {
    return false
  }

  return banner.schedules.some((schedule) => {
    try {
      const scheduleStartValue =
        schedule.start ||
        schedule.startDate ||
        schedule.date ||
        schedule.scheduleStart ||
        schedule.from
      if (!scheduleStartValue) return false

      const scheduleStart = parseDate(scheduleStartValue)
      if (!scheduleStart) return false

      const scheduleEndValue = schedule.end || schedule.endDate || schedule.to
      const scheduleEnd = scheduleEndValue ? parseDate(scheduleEndValue) : null

      if (startDate) {
        const startDateObj = parseDate(startDate)
        if (startDateObj && scheduleStart < startDateObj) return false
      }
      if (endDate && scheduleEnd) {
        const endDateObj = parseDate(endDate)
        if (endDateObj && scheduleEnd > endDateObj) return false
      }
      if (!isFutureDate(scheduleStartValue)) return false

      return true
    } catch {
      return false
    }
  })
}

/**
 * Check if a banner uses a given template (by ID or name).
 * @param {object} banner
 * @param {string} templateId
 * @param {string|undefined} templateName
 * @returns {boolean}
 */
function bannerUsesTemplate(banner, templateId, templateName) {
  if (!banner.variants || !Array.isArray(banner.variants)) {
    return false
  }

  return banner.variants.some((variant) => {
    if (variant.code?.templateId === templateId || variant.code?.snippetId === templateId) {
      return true
    }

    if (templateName && variant.templateName) {
      if (
        variant.templateName === templateName ||
        variant.templateName.toLowerCase() === templateName.toLowerCase()
      ) {
        return true
      }
    }

    const codeTemplateName = variant.code?.templateName
    if (templateName && codeTemplateName) {
      if (
        codeTemplateName === templateName ||
        codeTemplateName.toLowerCase() === templateName.toLowerCase()
      ) {
        return true
      }
    }

    const codeTemplateNameLower = variant.code?.templatename
    if (templateName && codeTemplateNameLower) {
      if (
        codeTemplateNameLower === templateName ||
        codeTemplateNameLower.toLowerCase() === templateName.toLowerCase()
      ) {
        return true
      }
    }

    if (templateName && variant.templatename) {
      if (
        variant.templatename === templateName ||
        variant.templatename.toLowerCase() === templateName.toLowerCase()
      ) {
        return true
      }
    }

    if (templateName && variant.name) {
      if (
        variant.name === templateName ||
        variant.name.toLowerCase() === templateName.toLowerCase()
      ) {
        return true
      }
    }

    if (variant.metadata && Array.isArray(variant.metadata)) {
      const metadataMatch = variant.metadata.some(
        (md) =>
          (md.type === 'TemplateId' && md.value === templateId) ||
          (md.type === 'SnippetId' && md.value === templateId) ||
          (md.type === 'TemplateName' &&
            templateName &&
            (md.value === templateName || md.value?.toLowerCase() === templateName.toLowerCase())) ||
          (md.type === 'Name' &&
            templateName &&
            (md.value === templateName || md.value?.toLowerCase() === templateName.toLowerCase()))
      )
      if (metadataMatch) return true
    }

    if (variant.code && typeof variant.code === 'string' && templateName) {
      if (variant.code.includes(templateName)) return true
    }

    if (
      variant.code?.raw &&
      typeof variant.code.raw === 'string' &&
      templateName &&
      variant.code.raw.includes(templateName)
    ) {
      return true
    }

    return false
  })
}

/**
 * Filter banners that are eligible for update (use template, have future schedules, match date range).
 * @param {object[]} banners
 * @param {string} templateId
 * @param {string|undefined} templateName
 * @param {string|undefined} startDate
 * @param {string|undefined} endDate
 * @returns {object[]}
 */
function filterBannersForUpdate(banners, templateId, templateName, startDate, endDate) {
  const items = Array.isArray(banners) ? banners : []
  const template = templateName ?? templateId

  return items.filter((banner) => {
    if (!bannerUsesTemplate(banner, templateId, template)) return false

    // Exclude if any schedule has started (today or past)
    if (banner.schedules && Array.isArray(banner.schedules)) {
      const hasTodayOrPastSchedule = banner.schedules.some((schedule) => {
        const startValue =
          schedule.start ||
          schedule.startDate ||
          schedule.date ||
          schedule.scheduleStart ||
          schedule.from
        return startValue && isTodayOrPast(startValue)
      })
      if (hasTodayOrPastSchedule) return false
    }

    if (startDate || endDate) {
      return bannerMatchesSchedule(banner, startDate, endDate)
    }

    // Must have at least one future schedule
    if (!banner.schedules || !Array.isArray(banner.schedules)) return false

    const now = new Date()
    const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())

    return banner.schedules.some((schedule) => {
      const startValue =
        schedule.start ||
        schedule.startDate ||
        schedule.date ||
        schedule.scheduleStart ||
        schedule.from
      if (!startValue) return false
      const scheduleStart = parseDate(startValue)
      if (!scheduleStart) return false
      const scheduleStartDate = new Date(
        scheduleStart.getFullYear(),
        scheduleStart.getMonth(),
        scheduleStart.getDate()
      )
      return scheduleStartDate > today
    })
  })
}

module.exports = {
  parseDate,
  isFutureDate,
  isTodayOrPast,
  bannerMatchesSchedule,
  bannerUsesTemplate,
  filterBannersForUpdate,
}
