const chalk = require('chalk')
const ora = require('ora')
const { listBanners, updateBanner, transpileCode } = require('../lib/api')
const webTranspile = require('../lib/transpile')
const resolveWebCodeType = webTranspile.resolveWebCodeType

function parseDate(dateValue) {
  if (!dateValue) return null
  
  // If it's already a Date object, return it
  if (dateValue instanceof Date) {
    return isNaN(dateValue.getTime()) ? null : dateValue
  }
  
  // Try parsing as ISO string
  const date = new Date(dateValue)
  if (!isNaN(date.getTime())) {
    return date
  }
  
  return null
}

function isFutureDate(dateString) {
  const date = parseDate(dateString)
  if (!date) return false
  
  const now = new Date()
  // Exclude today - only future dates
  const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
  const scheduleDate = new Date(date.getFullYear(), date.getMonth(), date.getDate())
  return scheduleDate > today
}

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
}

function bannerMatchesSchedule(banner, startDate, endDate) {
  if (!banner.schedules || !Array.isArray(banner.schedules)) {
    return false
  }

  return banner.schedules.some((schedule) => {
    try {
      // Try different possible date field names
      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 (error) {
      console.log(chalk.yellow(`Error checking schedule: ${error.message}`))
      return false
    }
  })
}

function bannerUsesTemplate(banner, templateId, templateName) {
  if (!banner.variants || !Array.isArray(banner.variants)) {
    return false
  }

  return banner.variants.some((variant) => {
    // Check for template ID match
    if (variant.code?.templateId === templateId || variant.code?.snippetId === templateId) {
      return true
    }
    
    // Check for template name match - prioritize variant.templateName and variant.code.templateName
    // Check variant.templateName first (primary location)
    if (templateName && variant.templateName) {
      if (variant.templateName === templateName || variant.templateName.toLowerCase() === templateName.toLowerCase()) {
        return true
      }
    }
    
    // Check variant.code.templateName (secondary location)
    const codeTemplateName = variant.code?.templateName
    if (templateName && codeTemplateName) {
      if (codeTemplateName === templateName || codeTemplateName.toLowerCase() === templateName.toLowerCase()) {
        return true
      }
    }
    
    // Check variant.code.templatename (lowercase variant)
    const codeTemplateNameLower = variant.code?.templatename
    if (templateName && codeTemplateNameLower) {
      if (codeTemplateNameLower === templateName || codeTemplateNameLower.toLowerCase() === templateName.toLowerCase()) {
        return true
      }
    }
    
    // Check variant.templatename (lowercase variant)
    if (templateName && variant.templatename) {
      if (variant.templatename === templateName || variant.templatename.toLowerCase() === templateName.toLowerCase()) {
        return true
      }
    }
    
    // Check variant.name as fallback
    if (templateName && variant.name) {
      if (variant.name === templateName || variant.name.toLowerCase() === templateName.toLowerCase()) {
        return true
      }
    }
    
    // Check metadata for template/snippet reference
    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
      }
    }
    
    // Check if variant code references the template name directly
    if (variant.code && typeof variant.code === 'string') {
      if (templateName && variant.code.includes(templateName)) {
        return true
      }
    }
    
    // Check raw code if it's a string
    if (variant.code?.raw && typeof variant.code.raw === 'string' && templateName) {
      if (variant.code.raw.includes(templateName)) {
        return true
      }
    }
    
    return false
  })
}

async function updateBannersCommand(templateId, options) {
  try {
    const { appId, startDate, endDate } = options

    if (!appId) {
      console.error(chalk.red('Error: --app-id is required'))
      process.exit(1)
    }

    const spinner = ora(`Updating banners for template ${templateId}...`).start()

    // Get template from CMS
    spinner.text = 'Fetching template...'
    const { listCodeLibraries } = require('../lib/api')
    const allLibs = await listCodeLibraries(appId)
    const templateLibrary = allLibs.find((lib) => lib.templateId === templateId || lib.snippetId === templateId)

    if (!templateLibrary) {
      spinner.fail(`Template ${templateId} not found`)
      process.exit(1)
    }

    // Get template name from CMS record (used for banner matching)
    const templateName = templateLibrary.name || templateLibrary.templateName || templateId

    // Get transpiled code
    const rawCode = templateLibrary.code?.raw || templateLibrary.code
    const codeType = resolveWebCodeType(
      String(rawCode || ''),
      templateLibrary.codeType || 'jsx',
    )
    
    // Try to get already-transpiled code first (if available)
    let transpiledCode = templateLibrary.code?.compiled
    
    // If no compiled code exists, try to transpile it
    if (!transpiledCode) {
      spinner.text = 'Transpiling code via CMS API...'
      try {
        // First try local transpilation (faster, works for non-template code)
        transpiledCode = webTranspile(rawCode, codeType, templateId)
      } catch (localError) {
        // If local transpilation fails (e.g., template syntax), use CMS API
        try {
          transpiledCode = await transpileCode(rawCode, codeType, appId)
        } catch (apiError) {
          spinner.fail(`Failed to transpile code: ${apiError.message}`)
          throw new Error(`Could not transpile code: ${apiError.message}`)
        }
      }
    }

    spinner.text = 'Fetching banners...'
    // Fetch all banners (published and unpublished)
    const bannersResponse = await listBanners(appId, {})
    
    // Handle different response formats (array or object)
    let banners = []
    if (Array.isArray(bannersResponse)) {
      banners = bannersResponse
    } else if (bannersResponse && typeof bannersResponse === 'object') {
      // If it's an object, check if it has a data property or similar
      if (bannersResponse.data && Array.isArray(bannersResponse.data)) {
        banners = bannersResponse.data
      } else if (bannersResponse.banners && Array.isArray(bannersResponse.banners)) {
        banners = bannersResponse.banners
      } else if (bannersResponse.items && Array.isArray(bannersResponse.items)) {
        banners = bannersResponse.items
      } else {
        // If it's an object, convert to array
        banners = Object.values(bannersResponse)
      }
    } else {
      spinner.fail('Invalid banners response format')
      throw new Error('Banners API returned invalid format')
    }


    // Filter banners
    const matchingBanners = banners.filter((banner) => {
      // Must use this template (check both templateId and templateName)
      if (!bannerUsesTemplate(banner, templateId, templateName)) {
        return false
      }

      // Check if banner has any schedule that starts today or in the past
      if (banner.schedules && Array.isArray(banner.schedules)) {
        const hasTodayOrPastSchedule = banner.schedules.some((schedule) => {
          const startDateValue = schedule.start || schedule.startDate || schedule.date || schedule.scheduleStart || schedule.from
          return startDateValue && isTodayOrPast(startDateValue)
        })
        if (hasTodayOrPastSchedule) {
          return false
        }
      }

      // Must have future schedule (if dates provided)
      if (startDate || endDate) {
        if (!bannerMatchesSchedule(banner, startDate, endDate)) {
          return false
        }
      } else {
        // Check if any schedule is in the future
        if (!banner.schedules || !Array.isArray(banner.schedules)) {
          return false
        }
        
        const now = new Date()
        const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
        
        const hasFutureSchedule = banner.schedules.some((schedule) => {
          try {
            // Try different possible date field names
            const startDateValue = schedule.start || schedule.startDate || schedule.date || schedule.scheduleStart || schedule.from
            if (!startDateValue) return false
            
            const scheduleStart = parseDate(startDateValue)
            if (!scheduleStart) return false
            const scheduleStartDate = new Date(scheduleStart.getFullYear(), scheduleStart.getMonth(), scheduleStart.getDate())
            return scheduleStartDate > today
          } catch (error) {
            return false
          }
        })
        
        if (!hasFutureSchedule) {
          return false
        }
      }

      console.log(chalk.green(`  ✓ Banner matches all criteria`))
      return true
    })

    if (matchingBanners.length === 0) {
      spinner.warn('No matching banners found')
      return
    }

    spinner.succeed(`Found ${matchingBanners.length} banner(s) to update`)

    // Display banners that will be updated
    console.log(chalk.blue('\nBanners to be updated:\n'))
    console.log(chalk.gray('─'.repeat(100)))
    
    matchingBanners.forEach((banner, index) => {
      const bannerId = banner.id || 'N/A'
      const bannerName = banner.name || 'Unnamed Banner'
      const bannerDescription = banner.description || 'No description'
      
      console.log(chalk.bold(`\n${index + 1}. ${bannerName}`))
      console.log(`   ${chalk.cyan('ID')}: ${chalk.bold(bannerId)}`)
      console.log(`   ${chalk.gray('Description')}: ${bannerDescription}`)
      
      if (banner.schedules && Array.isArray(banner.schedules) && banner.schedules.length > 0) {
        console.log(`   ${chalk.gray('Schedule(s)')}:`)
        banner.schedules.forEach((schedule, schedIndex) => {
          try {
            // Try different possible date field names
            const startDateValue = schedule.start || schedule.startDate || schedule.date || schedule.scheduleStart || schedule.from
            const endDateValue = schedule.end || schedule.endDate || schedule.to
            
            if (!startDateValue) {
              console.log(`      ${schedIndex + 1}. ${chalk.yellow('No start date')}`)
              return
            }
            
            const startDate = parseDate(startDateValue)
            if (!startDate) {
              console.log(`      ${schedIndex + 1}. ${chalk.yellow(`Invalid start date: ${startDateValue}`)}`)
              return
            }
            
            const endDate = endDateValue ? parseDate(endDateValue) : null
            const isFuture = isFutureDate(startDateValue)
            const statusColor = isFuture ? chalk.green : chalk.yellow
            const statusText = isFuture ? 'Future' : 'Past'
            
            console.log(`      ${schedIndex + 1}. ${statusColor(statusText)}: ${chalk.bold(startDate.toISOString().split('T')[0])}`)
            if (endDate) {
              console.log(`         End: ${chalk.bold(endDate.toISOString().split('T')[0])}`)
            } else {
              console.log(`         End: ${chalk.gray('No end date')}`)
            }
          } catch (error) {
            console.log(`      ${schedIndex + 1}. ${chalk.yellow(`Error displaying schedule: ${error.message}`)}`)
          }
        })
      } else {
        console.log(`   ${chalk.gray('Schedule')}: ${chalk.yellow('No schedules')}`)
      }
      
      if (banner.published !== undefined) {
        const publishedStatus = banner.published ? chalk.green('Published') : chalk.yellow('Unpublished')
        console.log(`   ${chalk.gray('Status')}: ${publishedStatus}`)
      }
    })
    
    console.log(chalk.gray('\n' + '─'.repeat(100)))
    
    // Ask for confirmation before proceeding
    const inquirer = require('inquirer')
    const prompt = inquirer.createPromptModule()
    const { confirm } = await prompt([
      {
        type: 'confirm',
        name: 'confirm',
        message: `\nProceed with updating ${matchingBanners.length} banner(s)?`,
        default: true,
      },
    ])
    
    if (!confirm) {
      console.log(chalk.yellow('\nUpdate cancelled.'))
      return
    }

    // Update each banner
    let updatedCount = 0
    let failedCount = 0
    for (const banner of matchingBanners) {
      const updateSpinner = ora(`Updating banner ${banner.id || banner.name}...`).start()

      try {
        // Update variants with new code
        const updatedVariants = banner.variants.map((variant) => {
          if (variant.code?.templateId === templateId || variant.code?.snippetId === templateId || 
              variant.templateName === templateName || variant.templateName?.toLowerCase() === templateName?.toLowerCase() ||
              variant.code?.templateName === templateName || variant.code?.templateName?.toLowerCase() === templateName?.toLowerCase() ||
              variant.metadata?.some((md) => 
                (md.type === 'TemplateId' && md.value === templateId) ||
                (md.type === 'SnippetId' && md.value === templateId) ||
                (md.type === 'TemplateName' && md.value === templateName)
              )) {
            // Build updated variant - code must be a string according to schema
            const updatedVariant = {
              ...variant,
              // code must be a string, use the raw code
              code: typeof rawCode === 'string' ? rawCode : String(rawCode || ''),
              templateName: typeof templateName === 'string' ? templateName : String(templateName),
            }
            
            // Ensure codeType is a string (defaults to 'js' in schema)
            if (updatedVariant.codeType === undefined) {
              updatedVariant.codeType = codeType || 'js'
            } else {
              updatedVariant.codeType = typeof updatedVariant.codeType === 'string' ? updatedVariant.codeType : String(updatedVariant.codeType)
            }
            
            // Ensure other string fields are strings
            if (updatedVariant.codePreview !== undefined) {
              updatedVariant.codePreview = typeof updatedVariant.codePreview === 'string' ? updatedVariant.codePreview : String(updatedVariant.codePreview || '')
            }
            if (updatedVariant.url !== undefined) {
              updatedVariant.url = typeof updatedVariant.url === 'string' ? updatedVariant.url : String(updatedVariant.url || '')
            }
            
            return updatedVariant
          }
          return variant
        })

        // Prepare update payload with all required fields according to schema
        const updatePayload = {
          // Required fields
          name: typeof banner.name === 'string' ? banner.name : (banner.name ? String(banner.name) : 'Unnamed Banner'),
          schedules: Array.isArray(banner.schedules) ? banner.schedules : [],
          published: typeof banner.published === 'boolean' ? banner.published : false,
          variants: updatedVariants,
        }

        // Required based on appType
        const appType = banner.appType || 'web'
        updatePayload.appType = typeof appType === 'string' ? appType : String(appType)
        
        if (appType === 'web') {
          // Required for web: webSlots, locations
          updatePayload.webSlots = Array.isArray(banner.webSlots) ? banner.webSlots : (banner.webSlots ? [banner.webSlots] : [])
          updatePayload.locations = Array.isArray(banner.locations) ? banner.locations : (banner.locations ? [banner.locations] : [])
        } else if (appType === 'app') {
          // Required for app: location, devices
          updatePayload.location = typeof banner.location === 'string' ? banner.location : (banner.location ? String(banner.location) : '')
          updatePayload.devices = Array.isArray(banner.devices) ? banner.devices : (banner.devices ? [banner.devices] : [])
        }

        // Optional fields
        if (banner.alternateText !== undefined) {
          updatePayload.alternateText = typeof banner.alternateText === 'string' ? banner.alternateText : String(banner.alternateText || '')
        }
        if (banner.order !== undefined) {
          updatePayload.order = typeof banner.order === 'number' ? banner.order : Number(banner.order || 1)
        }
        if (banner.orientation !== undefined) {
          updatePayload.orientation = typeof banner.orientation === 'string' ? banner.orientation : String(banner.orientation || '')
        }
        if (banner.archived !== undefined) {
          updatePayload.archived = typeof banner.archived === 'boolean' ? banner.archived : false
        }
        if (banner.deleted !== undefined) {
          updatePayload.deleted = typeof banner.deleted === 'boolean' ? banner.deleted : false
        }
        if (banner.screenTypes !== undefined) {
          updatePayload.screenTypes = Array.isArray(banner.screenTypes) ? banner.screenTypes : (banner.screenTypes ? [banner.screenTypes] : [])
        }
        if (banner.slotId !== undefined) {
          updatePayload.slotId = typeof banner.slotId === 'string' ? banner.slotId : String(banner.slotId)
        }
        if (banner.customerGroup !== undefined) {
          updatePayload.customerGroup = typeof banner.customerGroup === 'string' ? banner.customerGroup : String(banner.customerGroup)
        }

        // Debug: Log payload structure and types
        console.log(chalk.gray(`\nDebug: Update payload structure:`))
        console.log(chalk.gray(`  name: ${typeof updatePayload.name} = "${updatePayload.name}"`))
        console.log(chalk.gray(`  variants: ${Array.isArray(updatePayload.variants) ? 'array' : typeof updatePayload.variants} (${updatePayload.variants.length} items)`))
        updatePayload.variants.forEach((variant, idx) => {
          console.log(chalk.gray(`    Variant ${idx + 1}:`))
          console.log(chalk.gray(`      templateName: ${typeof variant.templateName} = "${variant.templateName}"`))
          console.log(chalk.gray(`      codeType: ${typeof variant.codeType} = "${variant.codeType}"`))
          if (variant.code) {
            console.log(chalk.gray(`      code keys: ${Object.keys(variant.code).join(', ')}`))
            Object.keys(variant.code).forEach(key => {
              const value = variant.code[key]
              const type = typeof value
              const preview = type === 'string' ? (value.length > 50 ? value.substring(0, 50) + '...' : value) : JSON.stringify(value).substring(0, 50)
              console.log(chalk.gray(`        code.${key}: ${type} = ${preview}`))
            })
          }
        })
        console.log(chalk.gray(`  Other fields:`))
        Object.keys(updatePayload).filter(k => k !== 'name' && k !== 'variants').forEach(key => {
          const value = updatePayload[key]
          const type = Array.isArray(value) ? 'array' : typeof value
          console.log(chalk.gray(`    ${key}: ${type} = ${JSON.stringify(value).substring(0, 100)}`))
        })

        await updateBanner(appId, banner.id, updatePayload)

        updateSpinner.succeed(`Updated banner ${banner.id || banner.name}`)
        updatedCount++
      } catch (error) {
        updateSpinner.fail(`Failed to update banner: ${error.message}`)
        failedCount++
      }
    }

    // Only show success message if there were no failures
    if (updatedCount > 0 && failedCount === 0) {
      console.log(chalk.green(`\n✓ Successfully updated ${updatedCount} banner(s)`))
    } else if (updatedCount > 0 && failedCount > 0) {
      console.log(chalk.yellow(`\n⚠ Partially updated: ${updatedCount} succeeded, ${failedCount} failed`))
      process.exit(1)
    } else {
      console.log(chalk.red(`\n✗ Failed to update banners`))
      process.exit(1)
    }
  } catch (error) {
    console.error(chalk.red(`\n✗ Update failed: ${error.message}`))
    process.exit(1)
  }
}

module.exports = updateBannersCommand

