/**
 * Wizard Context
 * 
 * Manages the setup wizard state across all admin pages.
 * Provides current step, navigation, completion tracking,
 * and the floating panel visibility.
 * 
 * @package BoostBot
 */

import { createContext, useContext, useState, useCallback, useEffect, useMemo, type ReactNode } from 'react'
import { useNavigate, useLocation } from 'react-router-dom'
import { useSettings, useUpdateSettings } from '@/hooks/useSettings'
import { useBots } from '@/hooks/useBots'
import { getWizardSteps, resolveStepRoute, type WizardStep } from '@/components/wizard/wizardSteps'
import { executePingTargets, STEP_PING_TARGETS } from '@/components/wizard/wizardPing'

interface WizardContextType {
  /** Whether the wizard is active (onboarding not completed) */
  isWizardActive: boolean
  /** All wizard steps */
  steps: WizardStep[]
  /** Current step index (0-based) */
  currentStepIndex: number
  /** Current step object */
  currentStep: WizardStep | null
  /** Progress map (step ID → completed) */
  progress: Record<string, boolean>
  /** Whether the step list is expanded */
  isExpanded: boolean
  /** Toggle expanded state */
  toggleExpanded: () => void
  /** Navigate to a specific step by index */
  goToStep: (index: number) => void
  /** Navigate to next step */
  goToNextStep: () => void
  /** Navigate to previous step */
  goToPrevStep: () => void
  /** Skip the current step */
  skipStep: () => void
  /** Mark a step as completed */
  markStepCompleted: (stepId: string) => void
  /** Complete the entire wizard */
  completeWizard: () => Promise<void>
  /** Default bot ID */
  defaultBotId: number | null
  /** Resolve a route template with actual bot ID */
  resolveRoute: (route: string) => string
  /** Current streaming message text (for typewriter) */
  streamingMessage: string
  /** Whether the streaming message is being typed */
  isStreaming: boolean
  /** Trigger a new streaming message */
  triggerStreamingMessage: (message: string) => void
}

const WizardContext = createContext<WizardContextType | null>(null)

export function WizardProvider({ children }: { children: ReactNode }) {
  const navigate = useNavigate()
  const location = useLocation()
  const { data: settingsData, isLoading: settingsLoading } = useSettings()
  const updateSettings = useUpdateSettings()
  const { data: botsData } = useBots()

  // Local state
  const [currentStepIndex, setCurrentStepIndex] = useState(0)
  const [isExpanded, setIsExpanded] = useState(false)
  const [streamingMessage, setStreamingMessage] = useState('')
  const [isStreaming, setIsStreaming] = useState(false)
  const [fullMessage, setFullMessage] = useState('')
  const [hasInitializedStep, setHasInitializedStep] = useState(false)
  const [isCompletingWizard, setIsCompletingWizard] = useState(false)

  const settings = settingsData?.data

  // Wizard is active when the floating toggle is ON (regardless of onboarding_completed)
  // Use !! to handle falsy values from PHP/WordPress (empty string, 0, "0", null, false)
  const isWizardActive = !settingsLoading && settings
    ? !!settings.floating_wizard_enabled && !isCompletingWizard
    : false

  // Reset initialization flag when wizard becomes inactive so it re-initializes if restarted
  useEffect(() => {
    if (!isWizardActive) {
      setHasInitializedStep(false)
    }
  }, [isWizardActive])

  // Get progress from settings
  const progress: Record<string, boolean> = useMemo(() => {
    if (!settings?.wizard_progress) return {}
    return settings.wizard_progress
  }, [settings?.wizard_progress])

  // Detect bot ID from current URL (if user is editing a bot)
  const currentEditingBotId = useMemo(() => {
    const match = location.pathname.match(/^\/bots\/(\d+)/)
    return match ? Number(match[1]) : null
  }, [location.pathname])

  // Find the default/fallback bot ID from API data
  // Priority: is_default bot → oldest bot (first in list, usually default)
  const fallbackBotId = useMemo(() => {
    if (!botsData?.data) return null
    // The API returns { bots: Bot[] } inside data
    const bots = (botsData.data as any)?.bots || (Array.isArray(botsData.data) ? botsData.data : [])
    if (!Array.isArray(bots) || bots.length === 0) return null
    const defaultBot = bots.find((b: any) => b.is_default === 1 || b.is_default === true)
    return defaultBot ? Number(defaultBot.id) : Number(bots[0].id)
  }, [botsData])

  // Smart bot ID: current editing bot > default/oldest bot
  const defaultBotId = currentEditingBotId || fallbackBotId

  // Resolve route templates
  const resolveRoute = useCallback((route: string) => {
    return resolveStepRoute(route, defaultBotId)
  }, [defaultBotId])

  const wizardSteps = useMemo(() => getWizardSteps(), [])

  const currentStep = wizardSteps[currentStepIndex] || null

  // Typewriter streaming effect
  useEffect(() => {
    if (!fullMessage) return

    setStreamingMessage('')
    setIsStreaming(true)
    let charIndex = 0

    const interval = setInterval(() => {
      charIndex++
      if (charIndex <= fullMessage.length) {
        setStreamingMessage(fullMessage.slice(0, charIndex))
      } else {
        clearInterval(interval)
        setIsStreaming(false)
      }
    }, 30)

    return () => clearInterval(interval)
  }, [fullMessage])

  // Trigger streaming message
  const triggerStreamingMessage = useCallback((message: string) => {
    setFullMessage(message)
  }, [])

  // When wizard becomes active and we haven't initialized, find the first incomplete step
  useEffect(() => {
    if (!isWizardActive || hasInitializedStep || settingsLoading) return

    // Find first incomplete critical step, or first incomplete step
    const firstIncompleteIndex = wizardSteps.findIndex(step => !progress[step.id])
    if (firstIncompleteIndex >= 0) {
      setCurrentStepIndex(firstIncompleteIndex)
    }
    setHasInitializedStep(true)
  }, [isWizardActive, progress, hasInitializedStep, settingsLoading])

  // Trigger streaming message when step changes
  useEffect(() => {
    if (currentStep && isWizardActive) {
      triggerStreamingMessage(currentStep.scriptedMessage)
    }
  }, [currentStepIndex, isWizardActive]) // eslint-disable-line react-hooks/exhaustive-deps

  // Save progress to backend
  const saveProgress = useCallback(async (newProgress: Record<string, boolean>) => {
    try {
      await updateSettings.mutateAsync({ wizard_progress: newProgress } as any)
    } catch (error) {
      console.error('Failed to save wizard progress:', error)
    }
  }, [updateSettings])

  // Mark step as completed
  const markStepCompleted = useCallback((stepId: string) => {
    if (progress[stepId]) return // Already completed
    const newProgress = { ...progress, [stepId]: true }
    saveProgress(newProgress)
  }, [progress, saveProgress])

  // Navigate to step
  const goToStep = useCallback((index: number) => {
    if (index < 0 || index >= wizardSteps.length) return
    const step = wizardSteps[index]
    setCurrentStepIndex(index)

    // Build updated progress: mark the step we're LEAVING as completed,
    // and for non-critical new steps, mark them as visited too.
    const newProgress = { ...progress }
    let changed = false

    // Mark departing step as completed (the user visited it and moved on)
    const leavingStep = wizardSteps[currentStepIndex]
    if (leavingStep && !newProgress[leavingStep.id]) {
      newProgress[leavingStep.id] = true
      changed = true
    }

    // Mark arriving non-critical step as visited
    if (!step.isCritical && !newProgress[step.id]) {
      newProgress[step.id] = true
      changed = true
    }

    if (changed) {
      saveProgress(newProgress)
    }

    const route = resolveRoute(step.route)
    // Parse the route into pathname and search
    const [pathname, search] = route.split('?')
    navigate({ pathname, search: search ? `?${search}` : '' })

    // Trigger ping animation on the target elements after navigation
    const pingTargets = STEP_PING_TARGETS[step.id]
    if (pingTargets) {
      executePingTargets(pingTargets)
    }
  }, [navigate, resolveRoute, progress, saveProgress, currentStepIndex])

  // Go to next step
  const goToNextStep = useCallback(() => {
    if (currentStepIndex < wizardSteps.length - 1) {
      goToStep(currentStepIndex + 1)
    }
  }, [currentStepIndex, goToStep])

  // Go to previous step
  const goToPrevStep = useCallback(() => {
    if (currentStepIndex > 0) {
      goToStep(currentStepIndex - 1)
    }
  }, [currentStepIndex, goToStep])

  // Skip current step
  const skipStep = useCallback(() => {
    const step = wizardSteps[currentStepIndex]
    if (step) {
      // Mark as completed even when skipping
      const newProgress = { ...progress, [step.id]: true }
      saveProgress(newProgress)
    }
    goToNextStep()
  }, [currentStepIndex, progress, saveProgress, goToNextStep])

  // Toggle expanded
  const toggleExpanded = useCallback(() => {
    setIsExpanded(prev => !prev)
  }, [])

  // Complete the entire wizard — marks onboarding done AND turns off the floating toggle
  const completeWizard = useCallback(async () => {
    setIsCompletingWizard(true)
    try {
      const newProgress = { ...progress, finish: true }
      await updateSettings.mutateAsync({
        onboarding_completed: true,
        floating_wizard_enabled: false,
        wizard_progress: newProgress,
      } as any)
      navigate('/settings')
    } catch (error) {
      setIsCompletingWizard(false)
      console.error('Failed to complete wizard:', error)
      throw error
    }
  }, [progress, updateSettings, navigate])

  const value: WizardContextType = {
    isWizardActive,
    steps: wizardSteps,
    currentStepIndex,
    currentStep,
    progress,
    isExpanded,
    toggleExpanded,
    goToStep,
    goToNextStep,
    goToPrevStep,
    skipStep,
    markStepCompleted,
    completeWizard,
    defaultBotId,
    resolveRoute,
    streamingMessage,
    isStreaming,
    triggerStreamingMessage,
  }

  return (
    <WizardContext.Provider value={value}>
      {children}
    </WizardContext.Provider>
  )
}

/**
 * Hook to access wizard context.
 * Returns null if used outside of WizardProvider.
 */
export function useWizard(): WizardContextType | null {
  return useContext(WizardContext)
}

/**
 * Hook to access wizard context (throws if not available).
 */
export function useWizardRequired(): WizardContextType {
  const ctx = useContext(WizardContext)
  if (!ctx) {
    throw new Error('useWizardRequired must be used within a WizardProvider')
  }
  return ctx
}
