import React, { useState, useEffect, useRef } from 'react'
import { useAuth } from './auth/AuthContext'
import LoadingError from './LoadingError'
import './TestWords.css'
import './Integrations.css'

const API_URL = '/api'

const DEFAULT_TEST_WORD_COUNT = 10

function TestWords({ onNavigate, wordCount: initialWordCount, configId: initialConfigId, maxCards: initialMaxCards, taskId: initialTaskId }) {
  const { authFetch } = useAuth()
  const wordCount = initialWordCount || DEFAULT_TEST_WORD_COUNT
  const configId = initialConfigId || null
  const maxCards = initialMaxCards || null
  const taskId = initialTaskId || null
  
  const [words, setWords] = useState([]) // Начальный пул всех слов (для статистики)
  const [testWords, setTestWords] = useState([]) // Пул слов для показа
  const [currentWord, setCurrentWord] = useState(null) // Текущее слово, которое показывается (уже удалено из пула)
  const [flippedCards, setFlippedCards] = useState(new Set())
  const [wordStats, setWordStats] = useState({}) // Локальная статистика
  const [cardsShown, setCardsShown] = useState(0) // Левый счётчик: кол-во показанных карточек
  const [totalAnswers, setTotalAnswers] = useState(0) // Кол-во полученных ответов
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState('')
  const [showPreview, setShowPreview] = useState(false) // Показывать ли экран предпросмотра
  const [showResults, setShowResults] = useState(false) // Показывать ли экран результатов
  
  const isFinishingRef = useRef(false)
  const wordStatsRef = useRef({})
  const processingRef = useRef(false)
  const cardsShownRef = useRef(0) // Синхронный счётчик для избежания race condition

  // Функция равномерного распределения слов в пуле с гарантией максимального расстояния между одинаковыми словами
  // excludeFirstWordId - ID слова, которое не должно быть первым в пуле (только что показанная карточка)
  const redistributeWordsEvenly = (currentPool, allWords, excludeFirstWordId = null) => {
    if (currentPool.length === 0 || allWords.length === 0) {
      return currentPool
    }

    // Подсчитываем, сколько раз каждое слово встречается в текущем пуле
    const wordCounts = {}
    currentPool.forEach(word => {
      wordCounts[word.id] = (wordCounts[word.id] || 0) + 1
    })

    // Получаем список уникальных слов, которые есть в пуле
    const uniqueWordIds = Object.keys(wordCounts).map(id => parseInt(id))
    const uniqueWords = allWords.filter(word => uniqueWordIds.includes(word.id))

    if (uniqueWords.length === 0) {
      return currentPool
    }

    // Проверяем, есть ли в пуле слова, отличные от исключаемого
    const hasOtherWords = uniqueWords.some(w => w.id !== excludeFirstWordId)
    const effectiveExcludeId = hasOtherWords ? excludeFirstWordId : null

    // Создаём массив всех экземпляров слов для распределения
    const allInstances = []
    for (const word of uniqueWords) {
      const count = wordCounts[word.id]
      for (let i = 0; i < count; i++) {
        allInstances.push({ ...word })
      }
    }

    // Перемешиваем экземпляры
    for (let i = allInstances.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1))
      ;[allInstances[i], allInstances[j]] = [allInstances[j], allInstances[i]]
    }

    // Используем жадный алгоритм: на каждую позицию выбираем слово,
    // которое максимально далеко от своего последнего появления
    const totalSlots = currentPool.length
    const newPool = new Array(totalSlots).fill(null)
    const lastPosition = {} // Последняя позиция каждого слова

    for (let pos = 0; pos < totalSlots; pos++) {
      let bestWord = null
      let bestWordIndex = -1
      let bestDistance = -1

      for (let i = 0; i < allInstances.length; i++) {
        const word = allInstances[i]
        
        // Для позиции 0: не выбираем исключаемое слово, если есть альтернативы
        if (pos === 0 && word.id === effectiveExcludeId) {
          // Проверяем, есть ли другие слова
          const hasAlternative = allInstances.some(w => w.id !== effectiveExcludeId)
          if (hasAlternative) {
            continue
          }
        }

        // Вычисляем расстояние от последнего появления этого слова
        const lastPos = lastPosition[word.id]
        const distance = lastPos === undefined ? totalSlots : (pos - lastPos)

        // Выбираем слово с максимальным расстоянием
        if (distance > bestDistance) {
          bestDistance = distance
          bestWord = word
          bestWordIndex = i
        }
      }

      if (bestWord !== null) {
        newPool[pos] = bestWord
        lastPosition[bestWord.id] = pos
        allInstances.splice(bestWordIndex, 1)
      }
    }

    // Финальная проверка: если на позиции 0 оказалось исключаемое слово, меняем его с ближайшим другим
    if (effectiveExcludeId !== null && newPool[0] && newPool[0].id === effectiveExcludeId) {
      for (let i = 1; i < newPool.length; i++) {
        if (newPool[i] && newPool[i].id !== effectiveExcludeId) {
          ;[newPool[0], newPool[i]] = [newPool[i], newPool[0]]
          break
        }
      }
    }

    // Пост-обработка: исправляем последовательные дубликаты (одинаковые слова подряд)
    let iterations = 0
    const maxIterations = totalSlots * 2 // Предотвращаем бесконечный цикл
    let hasConsecutiveDuplicates = true
    
    while (hasConsecutiveDuplicates && iterations < maxIterations) {
      hasConsecutiveDuplicates = false
      iterations++
      
      for (let i = 0; i < newPool.length - 1; i++) {
        if (newPool[i] && newPool[i + 1] && newPool[i].id === newPool[i + 1].id) {
          // Нашли последовательные дубликаты на позициях i и i+1
          // Ищем слово для обмена (не то же самое и не соседнее с дубликатом после обмена)
          let swapped = false
          
          for (let j = i + 2; j < newPool.length && !swapped; j++) {
            if (!newPool[j]) continue
            
            // Проверяем, что слово на позиции j отличается от дубликата
            if (newPool[j].id === newPool[i].id) continue
            
            // Проверяем, что после обмена не создадим новые дубликаты
            // Позиция j-1 (если существует) не должна иметь тот же id, что и newPool[i+1]
            // Позиция j+1 (если существует) не должна иметь тот же id, что и newPool[i+1]
            const wouldCreateDuplicateBefore = j > 0 && newPool[j - 1] && newPool[j - 1].id === newPool[i + 1].id
            const wouldCreateDuplicateAfter = j < newPool.length - 1 && newPool[j + 1] && newPool[j + 1].id === newPool[i + 1].id
            
            if (!wouldCreateDuplicateBefore && !wouldCreateDuplicateAfter) {
              // Меняем местами
              ;[newPool[i + 1], newPool[j]] = [newPool[j], newPool[i + 1]]
              swapped = true
              hasConsecutiveDuplicates = true // Нужна ещё одна итерация для проверки
            }
          }
          
          // Если не нашли подходящую позицию справа, ищем слева
          if (!swapped) {
            for (let j = 0; j < i && !swapped; j++) {
              if (!newPool[j]) continue
              if (newPool[j].id === newPool[i].id) continue
              
              // Для позиции 0: не меняем на исключаемое слово
              if (j === 0 && newPool[i + 1].id === effectiveExcludeId) continue
              
              const wouldCreateDuplicateBefore = j > 0 && newPool[j - 1] && newPool[j - 1].id === newPool[i + 1].id
              const wouldCreateDuplicateAfter = j < newPool.length - 1 && newPool[j + 1] && newPool[j + 1].id === newPool[i + 1].id
              
              if (!wouldCreateDuplicateBefore && !wouldCreateDuplicateAfter) {
                ;[newPool[i + 1], newPool[j]] = [newPool[j], newPool[i + 1]]
                swapped = true
                hasConsecutiveDuplicates = true
              }
            }
          }
        }
      }
    }

    // Ещё раз проверяем позицию 0 после всех обменов
    if (effectiveExcludeId !== null && newPool[0] && newPool[0].id === effectiveExcludeId) {
      for (let i = 1; i < newPool.length; i++) {
        if (newPool[i] && newPool[i].id !== effectiveExcludeId) {
          // Проверяем, не создаст ли обмен дубликат на позиции 1
          if (i === 1 || (newPool[1] && newPool[1].id !== newPool[i].id)) {
            ;[newPool[0], newPool[i]] = [newPool[i], newPool[0]]
            break
          }
        }
      }
    }

    // Заполняем null-позиции (не должно происходить, но на всякий случай)
    for (let i = 0; i < newPool.length; i++) {
      if (newPool[i] === null && currentPool[i]) {
        newPool[i] = currentPool[i]
      }
    }

    return newPool
  }

  // Загрузка слов при монтировании
  useEffect(() => {
    setWords([])
    setTestWords([])
    setCurrentWord(null)
    setFlippedCards(new Set())
    setWordStats({})
    wordStatsRef.current = {}
    setCardsShown(0)
    cardsShownRef.current = 0 // Сбрасываем синхронный счётчик
    setTotalAnswers(0)
    setError('')
    setShowPreview(false) // Сбрасываем экран предпросмотра
    setShowResults(false) // Сбрасываем экран результатов при загрузке нового теста
    isFinishingRef.current = false
    processingRef.current = false
    setLoading(true)
    
    const loadWords = async () => {
      try {
        if (configId === null) {
          throw new Error('config_id обязателен для запуска теста')
        }
        const url = `${API_URL}/test/words?config_id=${configId}`
        const response = await authFetch(url)
        if (!response.ok) {
          throw new Error('Ошибка при загрузке слов')
        }
        const data = await response.json()
        
        if (!Array.isArray(data) || data.length === 0) {
          throw new Error('Недостаточно слов для теста')
        }

        // Инициализируем статистику из данных бэкенда
        const stats = {}
        data.forEach(word => {
          stats[word.id] = {
            success: word.success || 0,
            failure: word.failure || 0,
            lastSuccessAt: word.last_success_at || null,
            lastFailureAt: word.last_failure_at || null
          }
        })

        setWords(data)
        
        // Формируем пул слов: каждое слово добавляется n раз, затем пул перемешивается
        // n = max(1, floor(0.7 * maxCards / количество_слов))
        const wordsCount = data.length
        const cardsCount = maxCards !== null && maxCards > 0 ? maxCards : wordsCount
        const n = Math.max(1, Math.floor(0.7 * cardsCount / wordsCount))
        
        // Создаем пул, где каждое слово повторяется n раз
        let wordPool = []
        for (let i = 0; i < n; i++) {
          wordPool.push(...data)
        }
        
        // Равномерно распределяем слова в пуле
        wordPool = redistributeWordsEvenly(wordPool, data)
        
        setTestWords(wordPool)
        setWordStats(stats)
        wordStatsRef.current = stats
        
        // Показываем экран предпросмотра
        setShowPreview(true)
        
        // Показываем первую карточку и увеличиваем левый счётчик (будет использовано после начала теста)
        setCardsShown(0)
      } catch (err) {
        setError(err.message)
      } finally {
        setLoading(false)
      }
    }
    
    loadWords()
  }, [wordCount, configId])

  // Правый счётчик: текущий размер пула + показанные карточки, но не больше maxCards
  const getRightCounter = () => {
    const total = testWords.length + cardsShown
    if (maxCards !== null && maxCards > 0) {
      return Math.min(total, maxCards)
    }
    return total
  }

  const handleCardFlip = (wordId) => {
    setFlippedCards(prev => {
      const newSet = new Set(prev)
      if (newSet.has(wordId)) {
        newSet.delete(wordId)
      } else {
        newSet.add(wordId)
      }
      return newSet
    })
  }

  // Завершение теста
  const finishTest = async () => {
    if (isFinishingRef.current) return
    isFinishingRef.current = true
    
    // Сразу показываем экран результатов, чтобы предотвратить показ новых карточек
    setShowResults(true)
    
    // Отправляем статистику на бэкенд
    try {
      // Получаем актуальные данные из состояния
      const currentStats = wordStatsRef.current
      
      // Отправляем все слова, которые были в тесте, с их текущими значениями
      // Бэкенд сам обновит только измененные поля
      const updates = words.map(word => {
        const stats = currentStats[word.id] || {
          success: word.success || 0,
          failure: word.failure || 0,
          lastSuccessAt: word.last_success_at || null,
          lastFailureAt: word.last_failure_at || null
        }
        return {
          id: word.id,
          success: stats.success || 0,
          failure: stats.failure || 0,
          last_success_at: stats.lastSuccessAt || null,
          last_failure_at: stats.lastFailureAt || null
        }
      })

      if (updates.length === 0) {
        console.log('No words to send - empty test')
        return
      }

      const requestBody = { words: updates }
      if (configId !== null) {
        requestBody.config_id = configId
      }

      console.log('Sending test progress to backend:', {
        wordsCount: updates.length,
        configId: configId,
        requestBody
      })

      const response = await authFetch(`${API_URL}/test/progress`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(requestBody),
      })

      if (!response.ok) {
        const errorText = await response.text()
        throw new Error(`Server responded with status ${response.status}: ${errorText}`)
      }

      const responseData = await response.json().catch(() => ({}))
      console.log('Test progress saved successfully:', responseData)
      
      // Если есть taskId, выполняем задачу
      if (taskId) {
        try {
          const completeResponse = await authFetch(`${API_URL}/tasks/${taskId}/complete`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({}),
          })
          
          if (completeResponse.ok) {
            console.log('Task completed successfully')
          } else {
            console.error('Failed to complete task:', await completeResponse.text())
          }
        } catch (taskErr) {
          console.error('Failed to complete task:', taskErr)
        }
      }
    } catch (err) {
      console.error('Failed to save progress:', err)
      // Можно показать уведомление пользователю, но не блокируем показ результатов
    }
  }

  // Берём карточку из пула (getAndDelete) и показываем её
  const showNextCard = () => {
    // Проверяем, не завершился ли тест
    if (isFinishingRef.current) {
      return
    }
    
    // Используем функциональное обновление для получения актуального состояния пула
    setTestWords(prevPool => {
      // Повторная проверка внутри callback (на случай если состояние изменилось)
      if (isFinishingRef.current) {
        return prevPool
      }
      
      // Используем ref для синхронного доступа к счётчику
      const nextCardsShown = cardsShownRef.current + 1
      
      // Условие 1: Достигли максимума карточек
      if (maxCards !== null && maxCards > 0 && nextCardsShown > maxCards) {
        finishTest()
        return prevPool
      }
      
      // Условие 2: Пул слов пуст
      if (prevPool.length === 0) {
        finishTest()
        return prevPool
      }
      
      // getAndDelete: берём слово из пула и удаляем его
      const nextWord = prevPool[0]
      
      // Условие 3: Первое слово в пуле null/undefined (не должно происходить, но на всякий случай)
      if (!nextWord) {
        // Ищем первое не-null слово в пуле
        const validWordIndex = prevPool.findIndex(w => w !== null && w !== undefined)
        if (validWordIndex === -1) {
          // Нет валидных слов - завершаем тест
          finishTest()
          return prevPool
        }
        // Берём валидное слово
        const validWord = prevPool[validWordIndex]
        const updatedPool = [...prevPool.slice(0, validWordIndex), ...prevPool.slice(validWordIndex + 1)]
        
        // Синхронно обновляем ref
        cardsShownRef.current = nextCardsShown
        
        setCurrentWord(validWord)
        setCardsShown(nextCardsShown)
        setFlippedCards(new Set())
        
        return updatedPool
      }
      
      const updatedPool = prevPool.slice(1)
      
      // Синхронно обновляем ref ПЕРЕД установкой state
      cardsShownRef.current = nextCardsShown
      
      // showCard: показываем карточку
      setCurrentWord(nextWord)
      setCardsShown(nextCardsShown)
      setFlippedCards(new Set())
      
      return updatedPool
    })
  }

  const handleSuccess = (wordId) => {
    if (processingRef.current || isFinishingRef.current || showResults) return
    processingRef.current = true
    
    const word = words.find(w => w.id === wordId)
    if (!word) {
      processingRef.current = false
      return
    }

    const now = new Date().toISOString()
    
    // Обновляем статистику: success + 1, lastSuccessAt = now
    const currentWordStats = wordStatsRef.current[wordId] || { success: 0, failure: 0 }
    const updatedStats = {
      ...wordStatsRef.current,
      [wordId]: {
        success: (currentWordStats.success || 0) + 1,
        failure: currentWordStats.failure || 0,
        lastSuccessAt: now,
        lastFailureAt: currentWordStats.lastFailureAt || null
      }
    }
    wordStatsRef.current = updatedStats
    setWordStats(updatedStats)
    
    // Увеличиваем счётчик ответов
    const newTotalAnswers = totalAnswers + 1
    setTotalAnswers(newTotalAnswers)
    
    // onSuccess: просто повторяем (showNextCard)
    // Карточка уже удалена из пула при показе, просто показываем следующую
    showNextCard()
    
    // Если тест завершился, не сбрасываем processingRef
    if (isFinishingRef.current) {
      return
    }
    
    processingRef.current = false
  }

  const handleFailure = (wordId) => {
    if (processingRef.current || isFinishingRef.current || showResults) return
    processingRef.current = true
    
    const word = words.find(w => w.id === wordId)
    if (!word) {
      processingRef.current = false
      return
    }

    const now = new Date().toISOString()
    
    // Обновляем статистику: failure + 1, lastFailureAt = now
    const currentWordStats = wordStatsRef.current[wordId] || { success: 0, failure: 0 }
    const updatedStats = {
      ...wordStatsRef.current,
      [wordId]: {
        success: currentWordStats.success || 0,
        failure: (currentWordStats.failure || 0) + 1,
        lastSuccessAt: currentWordStats.lastSuccessAt || null,
        lastFailureAt: now
      }
    }
    wordStatsRef.current = updatedStats
    setWordStats(updatedStats)
    
    // Увеличиваем счётчик ответов
    const newTotalAnswers = totalAnswers + 1
    setTotalAnswers(newTotalAnswers)
    
    // onFailure: возвращаем карточку в пул, сортируем, повторяем
    setTestWords(prevPool => {
      // cards.add(currentCard): возвращаем слово обратно в пул
      let newTestWords = [...prevPool, word]
      
      // cards.sort(): равномерно перераспределяем слова в пуле
      // Передаём wordId, чтобы текущая карточка не оказалась первой (следующей для показа)
      newTestWords = redistributeWordsEvenly(newTestWords, words, wordId)
      
      return newTestWords
    })
    
    // repeat(): показываем следующую карточку
    showNextCard()
    
    // Если тест завершился, не сбрасываем processingRef
    if (isFinishingRef.current) {
      return
    }
    
    processingRef.current = false
  }

  const handleClose = () => {
    onNavigate?.('tasks')
  }

  const handleStartTest = () => {
    setShowPreview(false)
    // Показываем первую карточку (берём из пула)
    showNextCard()
  }

  const handleFinish = () => {
    onNavigate?.('tasks')
  }

  const getRandomSide = (word) => {
    return word.id % 2 === 0 ? 'word' : 'translation'
  }

  return (
    <div className="test-container test-container-fullscreen">
      <button className="close-x-button" onClick={handleClose}>
        ✕
      </button>
      {showPreview ? (
        <div className="test-preview">
          <div className="preview-stats">
            {words.map((word) => {
              const stats = wordStats[word.id] || { success: 0, failure: 0 }
              return (
                <div key={word.id} className="preview-item">
                  <div className="preview-word-content">
                    <div className="preview-word-header">
                      <h3 className="preview-word">{word.name}</h3>
                    </div>
                    <div className="preview-translation">{word.translation}</div>
                  </div>
                  <div className="preview-stats-numbers">
                    <span className="preview-stat-success">{stats.success}</span>
                    <span className="preview-stat-separator"> | </span>
                    <span className="preview-stat-failure">{stats.failure}</span>
                  </div>
                </div>
              )
            })}
          </div>
          <div className="preview-actions">
            <button className="test-start-button" onClick={handleStartTest}>
              Начать
            </button>
          </div>
        </div>
      ) : showResults ? (
        <div className="test-results">
          <div className="results-stats">
            {words.map((word) => {
              const stats = wordStats[word.id] || { success: 0, failure: 0 }
              return (
                <div key={word.id} className="result-item">
                  <div className="result-word-content">
                    <div className="result-word-header">
                      <h3 className="result-word">{word.name}</h3>
                    </div>
                    <div className="result-translation">{word.translation}</div>
                  </div>
                  <div className="result-stats">
                    <span className="result-stat-success">{stats.success}</span>
                    <span className="result-stat-separator"> | </span>
                    <span className="result-stat-failure">{stats.failure}</span>
                  </div>
                </div>
              )
            })}
          </div>
          <div className="results-actions">
            <button className="test-finish-button" onClick={handleFinish}>
              Закончить
            </button>
          </div>
        </div>
      ) : (
        <div className="test-screen">
          {loading && (
            <div className="fixed inset-0 flex justify-center items-center">
              <div className="flex flex-col items-center">
                <div className="w-12 h-12 border-4 border-indigo-200 border-t-indigo-600 rounded-full animate-spin mb-4"></div>
                <div className="text-gray-600 font-medium">Загрузка...</div>
              </div>
            </div>
          )}
          {error && (
            <LoadingError onRetry={() => {
              setError('')
              setLoading(true)
              // Перезагружаем слова
              const loadWords = async () => {
                try {
                  if (configId === null) {
                    throw new Error('config_id обязателен для запуска теста')
                  }
                  const url = `${API_URL}/test/words?config_id=${configId}`
                  const response = await authFetch(url)
                  if (!response.ok) {
                    throw new Error('Ошибка при загрузке слов')
                  }
                  const data = await response.json()
                  
                  if (!Array.isArray(data) || data.length === 0) {
                    throw new Error('Недостаточно слов для теста')
                  }

                  const stats = {}
                  data.forEach(word => {
                    stats[word.id] = {
                      success: word.success || 0,
                      failure: word.failure || 0,
                      lastSuccessAt: word.last_success_at || null,
                      lastFailureAt: word.last_failure_at || null
                    }
                  })

                  setWords(data)
                  
                  const wordsCount = data.length
                  const cardsCount = maxCards !== null && maxCards > 0 ? maxCards : wordsCount
                  const n = Math.max(1, Math.floor(0.7 * cardsCount / wordsCount))
                  
                  let wordPool = []
                  for (let i = 0; i < n; i++) {
                    wordPool.push(...data)
                  }
                  
                  wordPool = redistributeWordsEvenly(wordPool, data)
                  
                  setTestWords(wordPool)
                  setWordStats(stats)
                  wordStatsRef.current = stats
                  setShowPreview(true)
                  setCardsShown(0)
                } catch (err) {
                  setError(err.message)
                } finally {
                  setLoading(false)
                }
              }
              loadWords()
            }} />
          )}
          {!loading && !error && !isFinishingRef.current && currentWord && (() => {
            const word = currentWord
            const isFlipped = flippedCards.has(word.id)
            const showSide = getRandomSide(word)
            
            return (
              <div className="test-card-container" key={word.id}>
                <div 
                  className={`test-card ${isFlipped ? 'flipped' : ''}`}
                  onClick={() => handleCardFlip(word.id)}
                >
                  <div className="test-card-front">
                    <div className="test-card-content">
                      {showSide === 'word' ? (
                        <div className="test-word">{word.name}</div>
                      ) : (
                        <div className="test-translation">{word.translation}</div>
                      )}
                    </div>
                  </div>
                  <div className="test-card-back">
                    <div className="test-card-content">
                      {showSide === 'word' ? (
                        <div className="test-translation">{word.translation}</div>
                      ) : (
                        <div className="test-word">{word.name}</div>
                      )}
                      <div className="test-card-actions">
                        <button
                          className="test-action-button success-button"
                          onClick={(e) => {
                            e.stopPropagation()
                            handleSuccess(word.id)
                          }}
                        >
                          ✓ Знаю
                        </button>
                        <button
                          className="test-action-button failure-button"
                          onClick={(e) => {
                            e.stopPropagation()
                            handleFailure(word.id)
                          }}
                        >
                          ✗ Не знаю
                        </button>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            )
          })()}
          {!loading && !error && (
            <div className="test-progress">
              <div className="progress-text">
                {cardsShown} / {getRightCounter()}
              </div>
            </div>
          )}
        </div>
      )}
    </div>
  )
}

export default TestWords
