import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Bot, Loader2, Send, User } from 'lucide-react'
import { Badge, Button, Card, Loader } from '../../components/common'
import { endpoints, getErrorMessage } from '../../api/client'
import { t, tf } from '../../lib/i18n'
import type { ConversationMessage } from '../../types'
import {
  areTechnicalQuestionCardsComplete,
  buildTechnicalQuestionSubmission,
  formatCoins,
  getSuggestedQuestionOptionIds,
  mergeConversationState,
  normalizeTechnicalQuestionCards,
  normalizeTechnicalQuestionResponses,
  normalizeUnderstandingPercent,
  sendConversationPolling,
  type ConversationResult,
} from './conversation-utils'

interface ConversationPhaseStepProps {
  mode: 'generation_technical' | 'generation_content'
  title: string
  subtitle: string
  assistantLabel?: string
  sessionId: string | null
  initialStructuredState: Record<string, unknown>
  initialSummary?: string
  context: Record<string, unknown>
  autoStartMessage: string
  onSessionChange: (sessionId: string) => void
  onStateChange: (state: Record<string, unknown>) => void
  onBack?: () => void
  onContinue: (state: Record<string, unknown>) => void
  onSkip: () => void
  continueLabel: string
  skipLabel: string
  submitting?: boolean
  renderSidebar: (args: {
    draftState: Record<string, unknown>
    ready: boolean
    sessionCoins: number
    balance: number | null
    updateDraftState: (
      updater: Record<string, unknown> | ((current: Record<string, unknown>) => Record<string, unknown>)
    ) => void
  }) => React.ReactNode
  renderSecondaryPanel?: (args: {
    draftState: Record<string, unknown>
    ready: boolean
    sessionCoins: number
    balance: number | null
    updateDraftState: (
      updater: Record<string, unknown> | ((current: Record<string, unknown>) => Record<string, unknown>)
    ) => void
  }) => React.ReactNode
}

export function ConversationPhaseStep({
  mode,
  title,
  subtitle,
  assistantLabel,
  sessionId,
  initialStructuredState,
  initialSummary = '',
  context,
  autoStartMessage,
  onSessionChange,
  onStateChange,
  onBack,
  onContinue,
  onSkip,
  continueLabel,
  skipLabel,
  submitting = false,
  renderSidebar,
  renderSecondaryPanel,
}: ConversationPhaseStepProps) {
  const [messages, setMessages] = useState<ConversationMessage[]>([])
  const [draftState, setDraftState] = useState<Record<string, unknown>>(initialStructuredState)
  const [input, setInput] = useState('')
  const [sending, setSending] = useState(false)
  const [loading, setLoading] = useState(true)
  const [sessionCoins, setSessionCoins] = useState(0)
  const [balance, setBalance] = useState<number | null>(null)
  const [statusMessage, setStatusMessage] = useState<string | null>(null)
  const lastUserMessageRef = useRef<{ text: string; showUser: boolean } | null>(null)
  const loadedSessionKeyRef = useRef('')
  const messagesRef = useRef<HTMLDivElement>(null)
  const abortControllerRef = useRef<AbortController | null>(null)

  useEffect(() => {
    return () => {
      abortControllerRef.current?.abort()
    }
  }, [])

  const ready = Boolean(draftState.ready)
  const activeSessionId = sessionId
  const resolvedAssistantLabel = assistantLabel?.trim() || t('Assistant')
  const understandingPercent = normalizeUnderstandingPercent(draftState.understanding_percent)
  const normalizedQuestionCards = useMemo(
    () => normalizeTechnicalQuestionCards(draftState.question_cards),
    [draftState.question_cards],
  )
  const questionCards = normalizedQuestionCards
  const questionResponses = useMemo(
    () => normalizeTechnicalQuestionResponses(draftState.question_responses),
    [draftState.question_responses],
  )
  const hasQuestionWidget = questionCards.length > 0 && !ready
  const canSubmitQuestionWidget = areTechnicalQuestionCardsComplete(questionCards, questionResponses)
  const visibleMessages = useMemo(
    () => messages.filter((message) => message.content !== autoStartMessage),
    [autoStartMessage, messages],
  )
  const lastAssistantMessage = useMemo(
    () => [...visibleMessages].reverse().find((message) => message.role === 'assistant') || null,
    [visibleMessages],
  )
  const lastAssistantContent = lastAssistantMessage?.content ?? ''
  const openQuestions = Array.isArray(draftState.open_questions) ? draftState.open_questions : []
  const shouldShowQuestionFallback = !hasQuestionWidget
    && !ready
    && openQuestions.length > 0
    && Boolean(lastAssistantContent)
  const questionWidgetTitle = mode === 'generation_technical'
    ? t('Answer the open technical questions')
    : t('Answer the open planning questions')
  const questionWidgetDescription = mode === 'generation_technical'
    ? t('Choose an answer for each question, add details where needed, and then submit everything together.')
    : t('Choose the best options, keep or change the suggested answers, and add extra context where helpful.')
  const showQuestionSidebar = hasQuestionWidget

  const updateDraftState = useCallback((
    updater: Record<string, unknown> | ((current: Record<string, unknown>) => Record<string, unknown>),
  ) => {
    setDraftState((current) => {
      const nextState = typeof updater === 'function'
        ? updater(current)
        : updater
      onStateChange(nextState)
      return nextState
    })
  }, [onStateChange])

  const applyAssistantResult = useCallback((result: ConversationResult) => {
    setMessages((current) => [...current, { role: 'assistant', content: result.text }])
    setDraftState((current) => {
      const nextState = mergeConversationState(current, result.controlPayload)
      onStateChange(nextState)
      return nextState
    })

    const coinsUsed = Number(result.usage?.coins_used ?? 0)
    if (coinsUsed > 0) {
      setSessionCoins((current) => current + coinsUsed)
      setBalance((current) => current === null ? current : current - coinsUsed)
    }
  }, [onStateChange])

  const loadBalance = useCallback(async () => {
    try {
      const credits = await endpoints.getCreditsStatus()
      const cd = credits.data as any
      const freeRemaining = Math.max(0, (cd?.credits?.free?.total ?? 0) - (cd?.credits?.free?.used ?? 0))
      const purchasedRemaining = Math.max(0, (cd?.boost_credits?.total ?? 0) - (cd?.boost_credits?.used ?? 0))
      const subRemaining = Math.max(0, (cd?.credits?.subscription?.total ?? 0) - (cd?.credits?.subscription?.used ?? 0))
      setBalance(freeRemaining + purchasedRemaining + subRemaining)
    } catch {
      setBalance(null)
    }
  }, [])

  const openSession = useCallback(async () => {
    if (activeSessionId) {
      const res = await endpoints.getChatSession(activeSessionId)
      const data = res.data as any
      const loadedState = mergeConversationState(
        initialStructuredState,
        ((data.structured_state || {}) as Record<string, unknown>),
      )
      setMessages((data.messages as ConversationMessage[]) || [])
      setDraftState(loadedState)
      onStateChange(loadedState)
      return
    }

    const res = await endpoints.createChatSession({
      session_type: mode,
      structured_state: initialStructuredState,
      summary: initialSummary,
      wizard_context: context,
    })

    const data = res.data as any
    const createdSessionId = data.session_id as string
    onSessionChange(createdSessionId)
    setMessages((data.messages as ConversationMessage[]) || [])
    setDraftState((data.structured_state || initialStructuredState) as Record<string, unknown>)
    onStateChange((data.structured_state || initialStructuredState) as Record<string, unknown>)
  }, [activeSessionId, context, initialStructuredState, initialSummary, mode, onSessionChange, onStateChange])

  const sendMessage = useCallback(async (message: string, showUser = true) => {
    if (!activeSessionId) {
      return
    }

    setSending(true)
    setStatusMessage(null)
    lastUserMessageRef.current = { text: message, showUser }

    if (showUser) {
      setMessages((current) => [...current, { role: 'user', content: message }])
    }

    abortControllerRef.current?.abort()
    const controller = new AbortController()
    abortControllerRef.current = controller

    try {
      const result = await sendConversationPolling({
        mode,
        sessionId: activeSessionId,
        userMessage: message,
        context,
        structuredState: draftState,
        signal: controller.signal,
      })
      applyAssistantResult(result)
      lastUserMessageRef.current = null
    } catch (error) {
      if (error instanceof DOMException && error.name === 'AbortError') return
      const rawMsg = getErrorMessage(error, '')
      const isTokenLimit = rawMsg.includes('RESPONSE_TOO_LARGE') || rawMsg.includes('output limit')
      const isJsonError = !isTokenLimit && (rawMsg.includes('JSON') || rawMsg.includes('parse') || rawMsg.includes('position'))
      if (isTokenLimit) {
        setStatusMessage(t('The site has many custom fields and the AI response was too large. Please try again — this has been automatically adjusted.'))
      } else if (isJsonError) {
        setStatusMessage(t('The AI response had a formatting issue. Please try again.'))
      } else {
        setStatusMessage(rawMsg || t('AI response failed'))
      }
    } finally {
      setSending(false)
    }
  }, [activeSessionId, applyAssistantResult, context, draftState, mode])

  useEffect(() => {
    const nextLoadKey = `${mode}:${activeSessionId || 'new'}`
    if (loadedSessionKeyRef.current === nextLoadKey) {
      return
    }

    loadedSessionKeyRef.current = nextLoadKey
    setLoading(true)
    void (async () => {
      await loadBalance()
      await openSession()
      setLoading(false)
    })()
  }, [activeSessionId, loadBalance, mode, openSession])

  useEffect(() => {
    if (loading || !activeSessionId || messages.length > 0) {
      return
    }

    void sendMessage(autoStartMessage, false)
  }, [activeSessionId, autoStartMessage, loading, messages.length, sendMessage])

  useEffect(() => {
    if (!messagesRef.current) {
      return
    }

    messagesRef.current.scrollTop = messagesRef.current.scrollHeight
  }, [sending, statusMessage, visibleMessages])

  useEffect(() => {
    if (questionCards.length === 0) {
      return
    }

    const nextResponses = normalizeTechnicalQuestionResponses(draftState.question_responses)
    let changed = false

    questionCards.forEach((card) => {
      const currentResponse = nextResponses[card.id]
      if (currentResponse?.selected_option_ids.length) {
        return
      }

      const suggestedOptionIds = getSuggestedQuestionOptionIds(card)
      if (suggestedOptionIds.length === 0) {
        return
      }

      nextResponses[card.id] = {
        selected_option_ids: suggestedOptionIds,
        other_text: currentResponse?.other_text || '',
      }
      changed = true
    })

    if (!changed) {
      return
    }

    updateDraftState((current) => ({
      ...current,
      question_responses: nextResponses,
    }))
  }, [draftState.question_responses, questionCards, updateDraftState])

  const lastContextCountRef = useRef<number | null>(null)

  useEffect(() => {
    if (mode !== 'generation_content') {
      return
    }

    const count = typeof (context as Record<string, unknown>).count === 'number'
      ? (context as Record<string, unknown>).count as number
      : null
    if (count === null) {
      return
    }

    if (lastContextCountRef.current === null) {
      lastContextCountRef.current = count
      return
    }

    if (count === lastContextCountRef.current) {
      return
    }

    const previousCount = lastContextCountRef.current
    lastContextCountRef.current = count

    if (!activeSessionId || messages.length === 0 || loading) {
      return
    }

    void sendMessage(
      `[SYSTEM] ${tf('Important update: The number of articles has been changed from %d to %d. Please update the topic tree accordingly.', previousCount, count)}`,
      false,
    )
  }, [activeSessionId, context, loading, messages.length, mode, sendMessage])

  if (loading) {
    return (
      <div className="py-16 flex items-center justify-center">
        <Loader />
      </div>
    )
  }

  return (
    <div className="space-y-6">
      <div className={`grid grid-cols-1 gap-6 ${showQuestionSidebar ? 'xl:grid-cols-[minmax(0,1fr)_360px]' : ''}`}>
        <Card className="overflow-hidden p-0">
        <div className="border-b border-bc-gray-200 px-6 py-4">
          <h2 className="text-2xl font-bold text-bc-gray-800">{title}</h2>
          <p className="text-sm text-bc-gray-500 mt-1">{subtitle}</p>
          {mode === 'generation_technical' ? (
            <div className="mt-4">
              <div className="mb-1 flex items-center justify-between gap-3 text-xs text-bc-gray-500">
                <span>{t('Technical understanding')}</span>
                <span>{understandingPercent}%</span>
              </div>
              <div className="h-2 overflow-hidden rounded-full bg-bc-gray-200">
                <div
                  className="h-full rounded-full bg-bc-primary transition-all duration-300"
                  style={{ width: `${understandingPercent}%` }}
                />
              </div>
            </div>
          ) : null}
        </div>

        <div ref={messagesRef} className="min-h-[420px] max-h-[60vh] overflow-y-auto bg-bc-gray-50 px-6 py-5 space-y-4">
          {visibleMessages.map((message, index) => (
            <div key={`${message.role}-${index}`} className={`flex ${message.role === 'assistant' ? 'justify-start' : 'justify-end'}`}>
              <div className={`max-w-[85%] rounded-bc px-4 py-3 shadow-sm ${
                message.role === 'assistant' ? 'bg-white text-bc-gray-800' : 'bg-bc-primary text-white'
              }`}>
                <div className="mb-1 flex items-center gap-2 text-xs opacity-80">
                  {message.role === 'assistant' ? <Bot className="w-3.5 h-3.5" /> : <User className="w-3.5 h-3.5" />}
                  <span>{message.role === 'assistant' ? resolvedAssistantLabel : t('You')}</span>
                </div>
                <div className="whitespace-pre-wrap text-sm">{message.content}</div>
              </div>
            </div>
          ))}

          {sending ? (
            <div className="flex items-center gap-2 text-sm text-bc-gray-500">
              <Loader2 className="w-4 h-4 animate-spin" />
              {t('Generating response...')}
            </div>
          ) : null}

          {statusMessage ? (
            <div className="rounded-bc border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700 flex items-center justify-between gap-3">
              <span>{statusMessage}</span>
              {lastUserMessageRef.current && (
                <Button
                  size="sm"
                  variant="secondary"
                  onClick={() => {
                    const last = lastUserMessageRef.current
                    if (last) {
                      void sendMessage(last.text, false)
                    }
                  }}
                >
                  {t('Try again')}
                </Button>
              )}
            </div>
          ) : null}
        </div>

        <div className="border-t border-bc-gray-200 bg-white p-4">
          {shouldShowQuestionFallback ? (
            <div className="mb-3 rounded-bc border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800">
              {t('Question cards are unavailable for this reply. Use the text box below so the conversation can continue.')}
            </div>
          ) : null}
          <textarea
            value={input}
            onChange={(e) => setInput(e.target.value)}
            rows={3}
            disabled={sending}
            placeholder={t('Type your message...')}
            className="w-full rounded-bc border border-bc-gray-300 px-3 py-2"
          />

          <div className="mt-3 flex flex-wrap items-center justify-between gap-3">
            <div className="flex items-center gap-2 text-xs text-bc-gray-500">
              <span>{t('Session Cost')}: {formatCoins(sessionCoins)}</span>
              <span>•</span>
              <span>{t('Current Balance')}: {balance === null ? '—' : formatCoins(balance)}</span>
            </div>

            <div className="flex gap-2">
              {onBack ? (
                <Button variant="ghost" onClick={onBack}>
                  {t('Back')}
                </Button>
              ) : null}
              <Button variant="ghost" onClick={onSkip} disabled={submitting}>
                {skipLabel}
              </Button>
              <Button
                variant="secondary"
                onClick={() => onContinue(draftState)}
                disabled={!ready || submitting}
                loading={submitting}
              >
                {continueLabel}
              </Button>
              <Button
                onClick={() => {
                  const message = hasQuestionWidget
                    ? buildTechnicalQuestionSubmission(questionCards, questionResponses, input)
                    : input.trim()
                  if (!message || (hasQuestionWidget && !canSubmitQuestionWidget)) return
                  setInput('')
                  if (hasQuestionWidget) {
                    updateDraftState((current) => ({ ...current, question_cards: [], question_responses: {}, open_questions: [] }))
                  }
                  void sendMessage(message)
                }}
                disabled={sending || (hasQuestionWidget ? !canSubmitQuestionWidget : !input.trim())}
                icon={<Send className="w-4 h-4" />}
              >
                {hasQuestionWidget ? t('Submit answers') : t('Send')}
              </Button>
            </div>
          </div>
        </div>
        </Card>

        {showQuestionSidebar ? (
          <Card className="overflow-hidden p-0">
            <div className="border-b border-bc-gray-200 bg-white px-4 py-3">
              <div className="text-sm font-semibold text-bc-gray-800">{questionWidgetTitle}</div>
              <p className="mt-1 text-xs text-bc-gray-500">
                {questionWidgetDescription}
              </p>
            </div>
            <div className="max-h-[calc(60vh+140px)] space-y-3 overflow-y-auto bg-white px-3 py-3">
              {questionCards.map((card) => {
                const response = questionResponses[card.id] || {
                  selected_option_ids: [],
                  other_text: '',
                }
                const suggestedOptionIds = getSuggestedQuestionOptionIds(card)

                return (
                  <div key={card.id} className="rounded-bc border border-bc-gray-200 bg-bc-gray-50 p-2.5">
                    <div className="flex flex-wrap items-start justify-between gap-2">
                      <div className="text-sm font-medium text-bc-gray-800">{card.prompt}</div>
                      {suggestedOptionIds.length > 0 ? (
                        <Badge variant="primary">{t('Suggested answer')}</Badge>
                      ) : null}
                    </div>
                    {card.help_text ? <div className="mt-1 text-xs text-bc-gray-500">{card.help_text}</div> : null}
                    {card.selection_type === 'checkbox' ? (
                      <div className="mt-2 text-xs text-bc-gray-500">{t('Select all that apply')}</div>
                    ) : null}
                    <div className="mt-3 space-y-2">
                      {card.options.map((option) => {
                        const checked = response.selected_option_ids.includes(option.id)
                        const isSuggested = suggestedOptionIds.includes(option.id)
                        return (
                          <div
                            key={option.id}
                            role="button"
                            tabIndex={0}
                            onClick={() => updateDraftState((current) => {
                              const currentResponses = normalizeTechnicalQuestionResponses(current.question_responses)
                              const currentResponse = currentResponses[card.id] || {
                                selected_option_ids: [],
                                other_text: '',
                              }
                              const nextSelected = card.selection_type === 'checkbox'
                                ? (checked
                                  ? currentResponse.selected_option_ids.filter((item) => item !== option.id)
                                  : [...currentResponse.selected_option_ids, option.id])
                                : [option.id]

                              return {
                                ...current,
                                question_responses: {
                                  ...currentResponses,
                                  [card.id]: {
                                    selected_option_ids: nextSelected,
                                    other_text: currentResponse.other_text,
                                  },
                                },
                              }
                            })}
                            onKeyDown={(e) => {
                              if (e.key === 'Enter' || e.key === ' ') {
                                e.preventDefault()
                                e.currentTarget.click()
                              }
                            }}
                            className={`block cursor-pointer rounded-lg px-3 py-2.5 transition-all duration-150 ${
                              checked
                                ? 'border-[1.5px] border-bc-primary bg-bc-primary/[0.04] shadow-[inset_0_1px_3px_rgba(8,145,178,0.08)]'
                                : 'border-[1.5px] border-bc-gray-200 bg-white hover:border-bc-gray-300 hover:bg-black/[0.01]'
                            }`}
                          >
                            <div className="flex flex-wrap items-center gap-2">
                              <div className="text-sm font-medium text-bc-gray-800">{option.label}</div>
                              {isSuggested ? (
                                <span className="rounded-full bg-teal-50 px-1.5 py-0.5 text-[11px] font-medium text-teal-600">
                                  {t('Recommended')}
                                </span>
                              ) : null}
                            </div>
                            {option.description ? (
                              <div className="mt-1 text-xs text-bc-gray-500">{option.description}</div>
                            ) : null}
                          </div>
                        )
                      })}
                    </div>
                    <div className="mt-3">
                      <label className="mb-1 block text-xs font-medium text-bc-gray-700">
                        {t('Add a note or custom answer')}
                      </label>
                      <textarea
                        value={response.other_text}
                        onChange={(event) => updateDraftState((current) => {
                          const currentResponses = normalizeTechnicalQuestionResponses(current.question_responses)
                          const currentResponse = currentResponses[card.id] || {
                            selected_option_ids: [],
                            other_text: '',
                          }
                          return {
                            ...current,
                            question_responses: {
                              ...currentResponses,
                              [card.id]: {
                                selected_option_ids: currentResponse.selected_option_ids,
                                other_text: event.target.value,
                              },
                            },
                          }
                        })}
                        rows={3}
                        placeholder={t('Add a note or custom answer')}
                        className="w-full rounded-bc border border-bc-gray-300 px-3 py-2 text-sm"
                      />
                      {response.selected_option_ids.some((optionId) => {
                        const option = card.options.find((item) => item.id === optionId)
                        return Boolean(option?.requires_text)
                      }) && !response.other_text.trim() ? (
                        <div className="mt-1 text-xs text-amber-700">{t('Custom answer')}</div>
                      ) : null}
                    </div>
                  </div>
                )
              })}
            </div>
          </Card>
        ) : null}
      </div>

      {renderSecondaryPanel ? (
        <div className="grid grid-cols-1 gap-6 xl:grid-cols-[minmax(0,7fr)_minmax(280px,3fr)]">
          <div className="min-w-0">
            {renderSecondaryPanel({ draftState, ready, sessionCoins, balance, updateDraftState })}
          </div>
          <Card>
            <div className="mb-3 flex items-center justify-between">
              <h3 className="font-semibold text-bc-gray-800">{t('Current Draft')}</h3>
              {ready ? <Badge variant="success">{t('Ready')}</Badge> : <Badge variant="default">{t('In Progress')}</Badge>}
            </div>
            {renderSidebar({ draftState, ready, sessionCoins, balance, updateDraftState })}
          </Card>
        </div>
      ) : (
        <Card>
          <div className="mb-3 flex items-center justify-between">
            <h3 className="font-semibold text-bc-gray-800">{t('Current Draft')}</h3>
            {ready ? <Badge variant="success">{t('Ready')}</Badge> : <Badge variant="default">{t('In Progress')}</Badge>}
          </div>
          {renderSidebar({ draftState, ready, sessionCoins, balance, updateDraftState })}
        </Card>
      )}
    </div>
  )
}
