import React, { useState, useEffect, useMemo, useRef } from 'react'
import { createPortal } from 'react-dom'
import { useAuth } from './auth/AuthContext'
import TaskDetail from './TaskDetail'
import LoadingError from './LoadingError'
import Toast from './Toast'
import { DayPicker } from 'react-day-picker'
import { ru } from 'react-day-picker/locale'
import 'react-day-picker/style.css'
import './TaskList.css'

const API_URL = '/api/tasks'

function TaskList({ onNavigate, data, loading, backgroundLoading, error, onRetry, onRefresh }) {
  const { authFetch } = useAuth()
  // Инициализируем tasks из data, если data есть, иначе пустой массив
  const [tasks, setTasks] = useState(() => data && Array.isArray(data) ? data : [])
  const [selectedTaskForDetail, setSelectedTaskForDetail] = useState(null)
  const [isCompleting, setIsCompleting] = useState(false)
  const [expandedCompleted, setExpandedCompleted] = useState({})
  const [selectedTaskForPostpone, setSelectedTaskForPostpone] = useState(null)
  const [postponeDate, setPostponeDate] = useState('')
  const [isPostponing, setIsPostponing] = useState(false)
  const [toast, setToast] = useState(null)
  const [searchQuery, setSearchQuery] = useState('')
  // Режим группировки: 'project' (по проекту - по умолчанию) или 'group' (по группе)
  const [groupingMode, setGroupingMode] = useState(() => {
    // Восстанавливаем из localStorage, по умолчанию 'project'
    try {
      const saved = localStorage.getItem('taskListGroupingMode')
      return saved === 'group' ? 'group' : 'project'
    } catch {
      return 'project'
    }
  })

  // Сохраняем режим группировки в localStorage при изменении
  useEffect(() => {
    try {
      localStorage.setItem('taskListGroupingMode', groupingMode)
    } catch {
      // Игнорируем ошибки localStorage
    }
  }, [groupingMode])

  useEffect(() => {
    if (data) {
      setTasks(data)
    }
  }, [data])

  // Загрузка данных управляется из App.jsx через loadTabData
  // TaskList не инициирует загрузку самостоятельно

  const handleTaskClick = (task) => {
    setSelectedTaskForDetail(task.id)
  }

  const handleCheckmarkClick = async (task, e) => {
    e.stopPropagation()

    // Для задач-тестов запускаем тест вместо открытия модального окна
    const isTest = task.config_id != null
    if (isTest) {
      if (task.config_id) {
        try {
          // Загружаем детальную информацию о задаче, чтобы получить maxCards
          const response = await authFetch(`${API_URL}/${task.id}`)
          if (!response.ok) {
            throw new Error('Ошибка при загрузке деталей задачи')
          }
          const taskDetail = await response.json()

          // Переходим к тесту с maxCards
          onNavigate?.('test', {
            configId: task.config_id,
            taskId: task.id,
            maxCards: taskDetail.max_cards
          })
        } catch (err) {
          console.error('Failed to load task details:', err)
          // В случае ошибки всё равно переходим к тесту, но без maxCards
          onNavigate?.('test', { configId: task.config_id, taskId: task.id })
        }
      }
      return
    }

    // Для обычных задач открываем диалог подтверждения
    setSelectedTaskForDetail(task.id)
  }

  const handleCloseDetail = (skipHistoryBack = false) => {
    // Если skipHistoryBack = true (например, при навигации на форму редактирования),
    // просто закрываем модальное окно без history.back()
    if (!skipHistoryBack && historyPushedForDetailRef.current) {
      window.history.back()
    } else {
      historyPushedForDetailRef.current = false
      setSelectedTaskForDetail(null)
    }
  }

  // Добавляем запись в историю при открытии модальных окон и обрабатываем "назад"
  const historyPushedForDetailRef = useRef(false)
  const historyPushedForPostponeRef = useRef(false)
  const selectedTaskForDetailRef = useRef(selectedTaskForDetail)
  const selectedTaskForPostponeRef = useRef(selectedTaskForPostpone)
  
  // Обновляем refs при изменении значений
  useEffect(() => {
    selectedTaskForDetailRef.current = selectedTaskForDetail
    selectedTaskForPostponeRef.current = selectedTaskForPostpone
  }, [selectedTaskForDetail, selectedTaskForPostpone])

  useEffect(() => {
    if (selectedTaskForPostpone && !historyPushedForPostponeRef.current) {
      // Добавляем запись в историю при открытии модального окна переноса
      window.history.pushState({ modalOpen: true, type: 'task-postpone' }, '', window.location.href)
      historyPushedForPostponeRef.current = true
    } else if (!selectedTaskForPostpone) {
      historyPushedForPostponeRef.current = false
    }

    if (selectedTaskForDetail && !historyPushedForDetailRef.current) {
      // Добавляем запись в историю при открытии модального окна деталей задачи
      window.history.pushState({ modalOpen: true, type: 'task-detail' }, '', window.location.href)
      historyPushedForDetailRef.current = true
    } else if (!selectedTaskForDetail) {
      historyPushedForDetailRef.current = false
    }

    if (!selectedTaskForDetail && !selectedTaskForPostpone) return

    const handlePopState = (event) => {
      // Проверяем наличие модальных окон в DOM
      const taskDetailModal = document.querySelector('.task-detail-modal-overlay')
      const postponeModal = document.querySelector('.task-postpone-modal-overlay')
      
      // Используем refs для получения актуального состояния
      const currentTaskDetail = selectedTaskForDetailRef.current
      const currentPostpone = selectedTaskForPostponeRef.current
      
      // Сначала проверяем модальное окно переноса (если оно открыто поверх)
      if (currentPostpone || postponeModal) {
        setSelectedTaskForPostpone(null)
        setPostponeDate('')
        historyPushedForPostponeRef.current = false
        // Возвращаем запись для модального окна деталей задачи, если оно было открыто
        if (currentTaskDetail || taskDetailModal) {
          window.history.pushState({ modalOpen: true, type: 'task-detail' }, '', window.location.href)
        }
        return
      }
      
      // Если открыто модальное окно деталей задачи, закрываем его
      if (currentTaskDetail || taskDetailModal) {
        setSelectedTaskForDetail(null)
        historyPushedForDetailRef.current = false
        // Следующее нажатие "назад" обработается App.jsx нормально
        return
      }
    }

    window.addEventListener('popstate', handlePopState)
    return () => {
      window.removeEventListener('popstate', handlePopState)
    }
  }, [selectedTaskForDetail, selectedTaskForPostpone])



  // Функция для вычисления следующей даты по repetition_date
  const calculateNextDateFromRepetitionDate = (repetitionDateStr) => {
    if (!repetitionDateStr) return null
    
    const parts = repetitionDateStr.trim().split(/\s+/)
    if (parts.length < 2) return null
    
    const value = parts[0]
    const unit = parts[1].toLowerCase()
    const now = new Date()
    now.setHours(0, 0, 0, 0)
    
    switch (unit) {
      case 'week': {
        // N-й день недели (1=понедельник, 7=воскресенье)
        const dayOfWeek = parseInt(value, 10)
        if (isNaN(dayOfWeek) || dayOfWeek < 1 || dayOfWeek > 7) return null
        // JavaScript: 0=воскресенье, 1=понедельник... 6=суббота
        // Наш формат: 1=понедельник... 7=воскресенье
        // Конвертируем: наш 1 (Пн) -> JS 1, наш 7 (Вс) -> JS 0
        const targetJsDay = dayOfWeek === 7 ? 0 : dayOfWeek
        const currentJsDay = now.getDay()
        // Вычисляем дни до следующего вхождения (включая сегодня, если ещё не прошло)
        let daysUntil = (targetJsDay - currentJsDay + 7) % 7
        // Если сегодня тот же день, берём следующую неделю
        if (daysUntil === 0) daysUntil = 7
        const nextDate = new Date(now)
        nextDate.setDate(now.getDate() + daysUntil)
        return nextDate
      }
      case 'month': {
        // N-й день месяца
        const dayOfMonth = parseInt(value, 10)
        if (isNaN(dayOfMonth) || dayOfMonth < 1 || dayOfMonth > 31) return null
        
        // Ищем ближайшую дату с этим днём
        let searchDate = new Date(now)
        for (let i = 0; i < 12; i++) {
          const year = searchDate.getFullYear()
          const month = searchDate.getMonth()
          const lastDayOfMonth = new Date(year, month + 1, 0).getDate()
          const actualDay = Math.min(dayOfMonth, lastDayOfMonth)
          const candidateDate = new Date(year, month, actualDay)
          
          if (candidateDate > now) {
            return candidateDate
          }
          // Переходим к следующему месяцу
          searchDate = new Date(year, month + 1, 1)
        }
        return null
      }
      case 'year': {
        // MM-DD формат
        const dateParts = value.split('-')
        if (dateParts.length !== 2) return null
        const monthNum = parseInt(dateParts[0], 10)
        const day = parseInt(dateParts[1], 10)
        if (isNaN(monthNum) || isNaN(day) || monthNum < 1 || monthNum > 12 || day < 1 || day > 31) return null
        
        let year = now.getFullYear()
        let candidateDate = new Date(year, monthNum - 1, day)
        
        if (candidateDate <= now) {
          candidateDate = new Date(year + 1, monthNum - 1, day)
        }
        return candidateDate
      }
      default:
        return null
    }
  }

  // Функция для вычисления следующей даты по repetition_period
  const calculateNextDateFromRepetitionPeriod = (repetitionPeriodStr) => {
    if (!repetitionPeriodStr) return null
    
    const parts = repetitionPeriodStr.trim().split(/\s+/)
    if (parts.length < 2) return null
    
    const value = parseInt(parts[0], 10)
    if (isNaN(value) || value === 0) return null
    
    const unit = parts[1].toLowerCase()
    const now = new Date()
    now.setHours(0, 0, 0, 0)
    
    const nextDate = new Date(now)
    
    switch (unit) {
      case 'minute':
      case 'minutes':
      case 'mins':
      case 'min':
        nextDate.setMinutes(nextDate.getMinutes() + value)
        break
      case 'hour':
      case 'hours':
      case 'hrs':
      case 'hr':
        nextDate.setHours(nextDate.getHours() + value)
        break
      case 'day':
      case 'days':
        // PostgreSQL может возвращать недели как дни (например, "7 days" вместо "1 week")
        // Если количество дней кратно 7, обрабатываем как недели
        if (value % 7 === 0 && value >= 7) {
          const weeks = value / 7
          nextDate.setDate(nextDate.getDate() + weeks * 7)
        } else {
          nextDate.setDate(nextDate.getDate() + value)
        }
        break
      case 'week':
      case 'weeks':
      case 'wks':
      case 'wk':
        nextDate.setDate(nextDate.getDate() + value * 7)
        break
      case 'month':
      case 'months':
      case 'mons':
      case 'mon':
        nextDate.setMonth(nextDate.getMonth() + value)
        break
      case 'year':
      case 'years':
      case 'yrs':
      case 'yr':
        nextDate.setFullYear(nextDate.getFullYear() + value)
        break
      default:
        return null
    }
    
    return nextDate
  }

  // Форматирование даты в YYYY-MM-DD (локальное время, без смещения в UTC)
  const formatDateToLocal = (date) => {
    const year = date.getFullYear()
    const month = String(date.getMonth() + 1).padStart(2, '0')
    const day = String(date.getDate()).padStart(2, '0')
    return `${year}-${month}-${day}`
  }

  // Форматирование даты для отображения с понятными названиями
  const formatDateForDisplay = (dateStr) => {
    if (!dateStr) return ''
    
    // Парсим дату из формата YYYY-MM-DD
    const dateParts = dateStr.split('-')
    if (dateParts.length !== 3) return dateStr
    
    const yearNum = parseInt(dateParts[0], 10)
    const monthNum = parseInt(dateParts[1], 10) - 1 // месяцы в JS начинаются с 0
    const dayNum = parseInt(dateParts[2], 10)
    
    if (isNaN(yearNum) || isNaN(monthNum) || isNaN(dayNum)) return dateStr
    
    const targetDate = new Date(yearNum, monthNum, dayNum)
    targetDate.setHours(0, 0, 0, 0)
    
    const now = new Date()
    now.setHours(0, 0, 0, 0)
    
    const diffDays = Math.floor((targetDate - now) / (1000 * 60 * 60 * 24))
    
    // Сегодня
    if (diffDays === 0) {
      return 'Сегодня'
    }
    
    // Завтра
    if (diffDays === 1) {
      return 'Завтра'
    }
    
    // Вчера
    if (diffDays === -1) {
      return 'Вчера'
    }
    
    // Дни недели для ближайших дней из будущего (в пределах 7 дней)
    if (diffDays > 0 && diffDays <= 7) {
      const dayNames = ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота']
      const dayOfWeek = targetDate.getDay()
      return dayNames[dayOfWeek]
    }
    
    const monthNames = ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 
                       'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря']
    
    // Если это число из того же года - только день и месяц
    if (targetDate.getFullYear() === now.getFullYear()) {
      const displayDay = targetDate.getDate()
      const displayMonth = monthNames[targetDate.getMonth()]
      return `${displayDay} ${displayMonth}`
    }
    
    // Для других случаев - полная дата
    const displayDay = targetDate.getDate()
    const displayMonth = monthNames[targetDate.getMonth()]
    const displayYear = targetDate.getFullYear()
    return `${displayDay} ${displayMonth} ${displayYear}`
  }

  const handlePostponeClick = (task, e) => {
    e.stopPropagation()
    setSelectedTaskForPostpone(task)
    
    // Устанавливаем дату по умолчанию
    let defaultDate
    const now = new Date()
    now.setHours(0, 0, 0, 0)
    
    if (task.repetition_date) {
      // Для задач с repetition_date - вычисляем следующую подходящую дату
      const nextDate = calculateNextDateFromRepetitionDate(task.repetition_date)
      if (nextDate) {
        defaultDate = nextDate
      }
    } else if (task.repetition_period && !isZeroPeriod(task.repetition_period)) {
      // Для задач с repetition_period (не нулевым) - вычисляем следующую дату
      const nextDate = calculateNextDateFromRepetitionPeriod(task.repetition_period)
      if (nextDate) {
        defaultDate = nextDate
      }
    }
    
    if (!defaultDate) {
      // Без repetition_date/repetition_period или если не удалось вычислить - завтра
      defaultDate = new Date(now)
      defaultDate.setDate(defaultDate.getDate() + 1)
    }
    
    defaultDate.setHours(0, 0, 0, 0)
    setPostponeDate(formatDateToLocal(defaultDate))
  }

  const handlePostponeSubmit = async () => {
    if (!selectedTaskForPostpone || !postponeDate) return
    await handlePostponeSubmitWithDate(postponeDate)
  }

  const handlePostponeClose = () => {
    // Если была добавлена запись в историю, удаляем её через history.back()
    // Обработчик popstate закроет модальное окно и сбросит флаг
    if (historyPushedForPostponeRef.current) {
      window.history.back()
    } else {
      // Если записи не было, просто закрываем модальное окно
      setSelectedTaskForPostpone(null)
      setPostponeDate('')
    }
  }

  const handleDateSelect = (date) => {
    if (!date) return
    const formattedDate = formatDateToLocal(date)
    setPostponeDate(formattedDate)
    if (selectedTaskForPostpone) {
      handlePostponeSubmitWithDate(formattedDate)
    }
  }

  const handleDayClick = (day, modifiers) => {
    // Обрабатываем клик даже если дата уже выбрана
    if (day && !modifiers.disabled) {
      handleDateSelect(day)
    }
  }

  const handleTodayClick = () => {
    const today = new Date()
    today.setHours(0, 0, 0, 0)
    setPostponeDate(formatDateToLocal(today))
    // Применяем дату сразу
    if (selectedTaskForPostpone) {
      handlePostponeSubmitWithDate(formatDateToLocal(today))
    }
  }

  const handleTomorrowClick = () => {
    const tomorrow = new Date()
    tomorrow.setDate(tomorrow.getDate() + 1)
    tomorrow.setHours(0, 0, 0, 0)
    setPostponeDate(formatDateToLocal(tomorrow))
    // Применяем дату сразу
    if (selectedTaskForPostpone) {
      handlePostponeSubmitWithDate(formatDateToLocal(tomorrow))
    }
  }

  const handlePostponeSubmitWithDate = async (dateToUse) => {
    if (!selectedTaskForPostpone || !dateToUse) return

    setIsPostponing(true)
    try {
      // Преобразуем дату в ISO формат с временем
      const dateObj = new Date(dateToUse)
      dateObj.setHours(0, 0, 0, 0)
      const isoDate = dateObj.toISOString()

      const response = await authFetch(`${API_URL}/${selectedTaskForPostpone.id}/postpone`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ next_show_at: isoDate }),
      })

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}))
        throw new Error(errorData.message || 'Ошибка при переносе задачи')
      }

      // Обновляем список
      if (onRefresh) {
        onRefresh()
      }

      // Закрываем модальное окно и удаляем запись из истории, если она была добавлена
      // Обработчик popstate закроет модальное окно и сбросит флаг
      if (historyPushedForPostponeRef.current) {
        window.history.back()
      } else {
        setSelectedTaskForPostpone(null)
        setPostponeDate('')
      }
    } catch (err) {
      console.error('Error postponing task:', err)
      setToast({ message: err.message || 'Ошибка при переносе задачи', type: 'error' })
    } finally {
      setIsPostponing(false)
    }
  }

  const handleWithoutDateClick = async () => {
    if (!selectedTaskForPostpone) return

    setIsPostponing(true)
    try {
      const response = await authFetch(`${API_URL}/${selectedTaskForPostpone.id}/postpone`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ next_show_at: null }),
      })

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}))
        throw new Error(errorData.message || 'Ошибка при переносе задачи')
      }

      if (onRefresh) {
        onRefresh()
      }

      if (historyPushedForPostponeRef.current) {
        window.history.back()
      } else {
        setSelectedTaskForPostpone(null)
        setPostponeDate('')
      }
    } catch (err) {
      console.error('Error postponing task:', err)
      setToast({ message: err.message || 'Ошибка при переносе задачи', type: 'error' })
    } finally {
      setIsPostponing(false)
    }
  }

  const toggleCompletedExpanded = (projectName) => {
    setExpandedCompleted(prev => ({
      ...prev,
      [projectName]: !prev[projectName]
    }))
  }

  // Получаем все проекты из задачи (теперь они приходят в task.project_names)
  const getTaskProjects = (task) => {
    if (task.project_names && Array.isArray(task.project_names)) {
      return task.project_names
    }
    return []
  }

  // Получаем название группы задачи (для режима группировки по группе)
  const getTaskGroupName = (task) => {
    // Если у задачи есть group_name - возвращаем его
    if (task.group_name && task.group_name.trim()) {
      return task.group_name.trim()
    }
    // Иначе возвращаем null - задача попадёт в "Остальные"
    return null
  }

  // Функция для проверки, является ли период нулевым
  const isZeroPeriod = (intervalStr) => {
    if (!intervalStr) return false
    
    const trimmed = intervalStr.trim()
    
    // Проверяем формат времени "00:00:00" или "0:00:00"
    if (/^\d{1,2}:\d{2}:\d{2}/.test(trimmed)) {
      const timeParts = trimmed.split(':')
      if (timeParts.length >= 3) {
        const hours = parseInt(timeParts[0], 10)
        const minutes = parseInt(timeParts[1], 10)
        const seconds = parseInt(timeParts[2], 10)
        return !isNaN(hours) && !isNaN(minutes) && !isNaN(seconds) && 
               hours === 0 && minutes === 0 && seconds === 0
      }
    }
    
    // PostgreSQL может возвращать "0 day", "0 days", "0", и т.д.
    const parts = trimmed.split(/\s+/)
    if (parts.length < 1) return false
    
    const value = parseInt(parts[0], 10)
    return !isNaN(value) && value === 0
  }

  // Функция для проверки, является ли repetition_date нулевым
  const isZeroDate = (dateStr) => {
    if (!dateStr) return false
    
    const trimmed = dateStr.trim()
    const parts = trimmed.split(/\s+/)
    if (parts.length < 2) return false
    
    const value = parts[0]
    // Проверяем, является ли значение "0" (для формата "0 week", "0 month", "0 year")
    const numValue = parseInt(value, 10)
    return !isNaN(numValue) && numValue === 0
  }

  // Группируем задачи по проектам или группам
  const groupedTasks = useMemo(() => {
    const today = new Date()
    today.setHours(0, 0, 0, 0)

    // Фильтруем задачи по поисковому запросу
    const filteredTasks = searchQuery.trim()
      ? tasks.filter(task => 
          task.name.toLowerCase().includes(searchQuery.toLowerCase())
        )
      : tasks

    const groups = {}
    
    filteredTasks.forEach(task => {
      let groupKeys = []
      
      if (groupingMode === 'project') {
        // Группировка по проекту (текущее поведение)
        groupKeys = getTaskProjects(task)
        if (groupKeys.length === 0) {
          groupKeys = ['Остальные']  // Было 'Без проекта'
        }
      } else {
        // Группировка по group_name
        const groupName = getTaskGroupName(task)
        groupKeys = groupName ? [groupName] : ['Остальные']
      }
      
      // Определяем, в какую группу попадает задача
      let isCompleted = false
      
      // Сначала проверяем, является ли задача бесконечной
      // Бесконечная задача: repetition_period == 0 И (repetition_date == 0 ИЛИ отсутствует)
      // Для обратной совместимости: если repetition_period = 0, считаем бесконечной
      const hasZeroPeriod = task.repetition_period && isZeroPeriod(task.repetition_period)
      const hasZeroDate = task.repetition_date && isZeroDate(task.repetition_date)
      // Идеально: оба поля = 0, но для старых задач может быть только repetition_period = 0
      const isInfinite = (hasZeroPeriod && hasZeroDate) || (hasZeroPeriod && !task.repetition_date)
      
      // Бесконечные задачи всегда идут в completed, независимо от next_show_at
      if (isInfinite) {
        isCompleted = true
      } else if (task.next_show_at) {
        // Для обычных задач используем next_show_at для группировки
        const nextShowDate = new Date(task.next_show_at)
        nextShowDate.setHours(0, 0, 0, 0)
        isCompleted = nextShowDate.getTime() > today.getTime()
      } else {
        // Задачи без даты (next_show_at = null) идут в выполненные
        isCompleted = true
      }
      
      groupKeys.forEach(groupKey => {
        if (!groups[groupKey]) {
          groups[groupKey] = {
            notCompleted: [],
            completed: []
          }
        }
        
        if (isCompleted) {
          groups[groupKey].completed.push(task)
        } else {
          // Бесконечные задачи теперь идут в обычный список
          groups[groupKey].notCompleted.push(task)
        }
      })
    })
    
    // Сортируем задачи внутри каждой группы проекта
    Object.keys(groups).forEach(projectName => {
      const group = groups[projectName]
      
      // Сортируем невыполненные задачи: по completed DESC (больше завершений выше), затем по id ASC (раньше добавленные выше)
      group.notCompleted.sort((a, b) => {
        if (b.completed !== a.completed) {
          return b.completed - a.completed // DESC
        }
        return a.id - b.id // ASC
      })
      
      // Сортируем выполненные задачи: бесконечные первыми, затем по next_show_at ASC (ранние в начале), NULL в начале
      group.completed.sort((a, b) => {
        // Проверяем, является ли задача бесконечной
        const hasZeroPeriodA = a.repetition_period && isZeroPeriod(a.repetition_period)
        const hasZeroDateA = a.repetition_date && isZeroDate(a.repetition_date)
        const isInfiniteA = (hasZeroPeriodA && hasZeroDateA) || (hasZeroPeriodA && !a.repetition_date)
        
        const hasZeroPeriodB = b.repetition_period && isZeroPeriod(b.repetition_period)
        const hasZeroDateB = b.repetition_date && isZeroDate(b.repetition_date)
        const isInfiniteB = (hasZeroPeriodB && hasZeroDateB) || (hasZeroPeriodB && !b.repetition_date)
        
        // Бесконечные задачи идут первыми
        if (isInfiniteA && !isInfiniteB) return -1
        if (!isInfiniteA && isInfiniteB) return 1
        if (isInfiniteA && isInfiniteB) return 0
        
        // Для остальных: NULL значения идут последними
        if (!a.next_show_at && !b.next_show_at) return 0
        if (!a.next_show_at) return 1
        if (!b.next_show_at) return -1
        
        // Сравниваем даты
        const dateA = new Date(a.next_show_at).getTime()
        const dateB = new Date(b.next_show_at).getTime()
        return dateA - dateB // ASC
      })
    })
    
    return groups
  }, [tasks, searchQuery, groupingMode])

  // Сортируем проекты: сначала с невыполненными задачами, потом без них
  // Группа "Без проекта" всегда последняя в своей категории
  const projectNames = useMemo(() => {
    const sorted = Object.keys(groupedTasks).sort((a, b) => {
      const groupA = groupedTasks[a]
      const groupB = groupedTasks[b]
      const hasNotCompletedA = groupA.notCompleted.length > 0
      const hasNotCompletedB = groupB.notCompleted.length > 0
      
      // Если у одной группы есть невыполненные, а у другой нет - сортируем по этому признаку
      if (hasNotCompletedA && !hasNotCompletedB) return -1
      if (!hasNotCompletedA && hasNotCompletedB) return 1
      
      // Если обе группы в одной категории
      const isOthersA = a === 'Остальные'
      const isOthersB = b === 'Остальные'
      
      // "Остальные" всегда последняя в своей категории
      if (isOthersA && !isOthersB) return 1
      if (!isOthersA && isOthersB) return -1
      
      // Остальные группы сортируем по алфавиту
      return a.localeCompare(b)
    })
    return sorted
  }, [groupedTasks])

  const renderTaskItem = (task, isCompleted = false) => {
    const hasProgression = task.has_progression || task.progression_base != null
    const hasSubtasks = task.subtasks_count > 0
    const isTest = task.config_id != null
    const showDetailOnCheckmark = !isTest
    const isWishlist = task.wishlist_id != null
    
    // Проверяем бесконечную задачу: repetition_period = 0 И (repetition_date = 0 ИЛИ отсутствует)
    // Для обратной совместимости: если repetition_period = 0, считаем бесконечной
    const hasZeroPeriod = task.repetition_period && isZeroPeriod(task.repetition_period)
    const hasZeroDate = task.repetition_date && isZeroDate(task.repetition_date)
    // Бесконечная задача: repetition_period = 0 И (repetition_date = 0 ИЛИ отсутствует)
    // Не проверяем next_show_at, так как для бесконечных задач он может быть установлен при выполнении
    const isInfinite = (hasZeroPeriod && hasZeroDate) || (hasZeroPeriod && !task.repetition_date)
    
    // Одноразовая задача: когда оба поля null/undefined
    const isOneTime = (task.repetition_period == null || task.repetition_period === undefined) && 
                      (task.repetition_date == null || task.repetition_date === undefined)

    return (
      <div
        key={task.id}
        className="task-item"
        onClick={() => handleTaskClick(task)}
      >
        <div className="task-item-content">
          <div
            className={`task-checkmark ${showDetailOnCheckmark ? 'task-checkmark-detail' : ''} ${task.auto_complete ? 'task-checkmark-auto-complete' : ''}`}
            onClick={(e) => handleCheckmarkClick(task, e)}
            title={isTest ? 'Запустить тест' : (showDetailOnCheckmark ? 'Открыть детали' : 'Выполнить задачу')}
          >
            {isTest ? (
              <svg
                width="20"
                height="20"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                strokeWidth="2"
                strokeLinecap="round"
                strokeLinejoin="round"
              >
                <path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path>
                <path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path>
              </svg>
            ) : isWishlist ? (
              <>
                <svg
                  width="20"
                  height="20"
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  strokeWidth="2"
                  strokeLinecap="round"
                  strokeLinejoin="round"
                >
                  <path d="M20 12v10M4 12v10M20 22H4"></path>
                  <path d="M22 7H2M22 7v5M2 7v5"></path>
                  <path d="M12 7H7.5a2.5 2.5 0 0 1 0-5C11 2 12 7 12 7z"></path>
                  <path d="M12 7h4.5a2.5 2.5 0 0 0 0-5C13 2 12 7 12 7z"></path>
                </svg>
                {task.auto_complete && (
                  <svg
                    className="task-checkmark-wishlist-lightning-icon"
                    width="16"
                    height="16"
                    viewBox="0 0 24 24"
                    fill="currentColor"
                    title="Задача-желание"
                  >
                    <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"></path>
                  </svg>
                )}
              </>
            ) : (
              <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
                <circle cx="10" cy="10" r="9" stroke="currentColor" strokeWidth="2" fill="none" className="checkmark-circle" />
                <path d="M6 10 L9 13 L14 7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="checkmark-check" />
              </svg>
            )}
            {task.auto_complete && !isTest && !isWishlist && (
              <svg
                className="task-checkmark-auto-complete-icon"
                width="16"
                height="16"
                viewBox="0 0 24 24"
                fill="currentColor"
                title="Автовыполнение в конце дня"
              >
                <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"></path>
              </svg>
            )}
          </div>
          <div className="task-name-container">
            <div className="task-name-wrapper">
              <div className="task-name">
                {task.name}
                {hasSubtasks && (
                  <span className="task-subtasks-count">(+{task.subtasks_count})</span>
                )}
                <span className="task-badge-bar">
                  {!isOneTime && !isInfinite && !isWishlist && (
                    <svg
                      className="task-recurring-icon"
                      width="16"
                      height="16"
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      strokeWidth="2"
                      strokeLinecap="round"
                      strokeLinejoin="round"
                      title="Повторяющаяся задача"
                    >
                      <path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/>
                      <path d="M21 3v5h-5"/>
                      <path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/>
                      <path d="M3 21v-5h5"/>
                    </svg>
                  )}
                  {isInfinite && (
                    <svg
                      className="task-infinite-icon"
                      width="16"
                      height="16"
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      strokeWidth="2"
                      strokeLinecap="round"
                      strokeLinejoin="round"
                      title="Бесконечная задача"
                    >
                      <path d="M12 12c0-2.5-1.5-4.5-3.5-4.5S5 9.5 5 12s1.5 4.5 3.5 4.5S12 14.5 12 12z"/>
                      <path d="M12 12c0 2.5 1.5 4.5 3.5 4.5S19 14.5 19 12s-1.5-4.5-3.5-4.5S12 9.5 12 12z"/>
                    </svg>
                  )}
                  {hasProgression && (
                    <svg
                      className="task-progression-icon"
                      width="16"
                      height="16"
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      strokeWidth="2"
                      strokeLinecap="round"
                      strokeLinejoin="round"
                      title="Задача с прогрессией"
                    >
                      <polyline points="23 6 13.5 15.5 8.5 10.5 1 18"></polyline>
                      <polyline points="17 6 23 6 23 12"></polyline>
                    </svg>
                  )}
                </span>
              </div>
              {/* Показываем дату только для выполненных задач */}
              {isCompleted && task.next_show_at && (() => {
                const showDate = new Date(task.next_show_at)
                // Нормализуем дату: устанавливаем время в 00:00:00 в локальном времени
                const showDateNormalized = new Date(showDate.getFullYear(), showDate.getMonth(), showDate.getDate())
                
                const today = new Date()
                const todayNormalized = new Date(today.getFullYear(), today.getMonth(), today.getDate())
                
                const tomorrowNormalized = new Date(todayNormalized)
                tomorrowNormalized.setDate(tomorrowNormalized.getDate() + 1)
                
                // Не показываем дату если она сегодня или в прошлом
                if (showDateNormalized.getTime() <= todayNormalized.getTime()) {
                  return null
                }
                
                let dateText
                if (showDateNormalized.getTime() === tomorrowNormalized.getTime()) {
                  dateText = 'Завтра'
                } else {
                  dateText = showDate.toLocaleDateString('ru-RU', { day: 'numeric', month: 'long', year: 'numeric' })
                }
                
                return (
                  <div className="task-next-show-date">
                    {dateText}
                  </div>
                )
              })()}
            </div>
          </div>
          <div className="task-actions">
            <button
              className="task-postpone-button"
              onClick={(e) => handlePostponeClick(task, e)}
              title="Перенести задачу"
            >
              <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
                <circle cx="10" cy="10" r="8" stroke="currentColor" strokeWidth="1.5" fill="none"/>
                <path d="M10 5V10L13 12" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" fill="none"/>
              </svg>
            </button>
          </div>
        </div>
      </div>
    )
  }

  // Показываем загрузку только если данных нет и это не фоновая загрузка
  // Проверяем наличие данных более надежно: либо в data, либо в tasks
  // Важно: проверяем оба источника данных, так как они могут обновляться асинхронно
  const hasDataInProps = data && Array.isArray(data) && data.length > 0
  const hasDataInState = tasks && Array.isArray(tasks) && tasks.length > 0
  const hasData = hasDataInProps || hasDataInState
  
  // Показываем ошибку загрузки, если есть ошибка и нет данных
  if (error && !hasData && !loading) {
    return (
      <div className="task-list">
        <LoadingError onRetry={onRetry} />
      </div>
    )
  }
  
  // Показываем загрузку только если:
  // 1. Идет загрузка (loading = true)
  // 2. Это не фоновая загрузка (backgroundLoading = false)
  // 3. Данных нет (hasData = false)
  // Это предотвращает показ загрузки при переключении табов, когда данные уже есть
  if (loading && !backgroundLoading && !hasData) {
    return (
      <div className="task-list">
        <div className="fixed inset-0 bottom-20 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>
      </div>
    )
  }

  return (
    <div className="task-list">
      {toast && (
        <Toast
          message={toast.message}
          type={toast.type || 'success'}
          onClose={() => setToast(null)}
        />
      )}

      {/* Поле поиска */}
      <div className="task-search-container">
        <svg
          className="task-search-icon"
          width="20"
          height="20"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          strokeWidth="2"
          strokeLinecap="round"
          strokeLinejoin="round"
        >
          <circle cx="11" cy="11" r="8"></circle>
          <path d="m21 21-4.35-4.35"></path>
        </svg>
        <input
          type="text"
          className="task-search-input"
          placeholder="Поиск задач..."
          value={searchQuery}
          onChange={(e) => setSearchQuery(e.target.value)}
        />
        {/* Кнопка переключения группировки */}
        <button
          type="button"
          className="task-grouping-toggle"
          onClick={() => setGroupingMode(prev => prev === 'project' ? 'group' : 'project')}
          title={groupingMode === 'project' ? 'Группировка по проекту' : 'Группировка по группе'}
        >
          {groupingMode === 'project' ? (
            // Иконка "папка" для группировки по проекту (filled)
            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
              <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path>
            </svg>
          ) : (
            // Иконка "тег" для группировки по группе (filled, с вырезом под дырку)
            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" fillRule="evenodd">
              <path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z M7 5.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3z"/>
            </svg>
          )}
        </button>
        {searchQuery && (
          <button
            className="task-search-clear"
            onClick={() => setSearchQuery('')}
            title="Очистить поиск"
          >
            ✕
          </button>
        )}
      </div>

      {projectNames.length === 0 && !loading && tasks.length === 0 && (
        <div className="empty-state">
          <p>Задач пока нет. Добавьте задачу через кнопку "Добавить".</p>
        </div>
      )}

      {projectNames.map(projectName => {
        const group = groupedTasks[projectName]
        const hasCompleted = group.completed.length > 0
        const hasNotCompleted = group.notCompleted.length > 0
        const isCompletedExpanded = expandedCompleted[projectName]

        return (
          <div key={projectName} className={`project-group ${!hasNotCompleted ? 'project-group-no-tasks' : ''}`}>
            <div 
              className={`project-group-header ${hasCompleted ? 'project-group-header-clickable' : ''}`}
              onClick={hasCompleted ? () => toggleCompletedExpanded(projectName) : undefined}
              title={hasCompleted ? (isCompletedExpanded ? 'Скрыть выполненные' : 'Показать выполненные') : undefined}
            >
              <h3 className={`project-group-title ${!hasNotCompleted ? 'project-group-title-empty' : ''}`}>{projectName}</h3>
              {hasCompleted ? (
                <button
                  className="completed-toggle-header"
                  onClick={(e) => {
                    e.stopPropagation()
                    toggleCompletedExpanded(projectName)
                  }}
                  title={isCompletedExpanded ? 'Скрыть выполненные' : 'Показать выполненные'}
                >
                  <span className="completed-toggle-icon">
                    {isCompletedExpanded ? '▼' : '▶'}
                  </span>
                </button>
              ) : (
                <div className="completed-toggle-header" style={{ visibility: 'hidden', pointerEvents: 'none' }}>
                  <span className="completed-toggle-icon">▶</span>
                </div>
              )}
            </div>
            
            {/* Обычные задачи (включая бесконечные) */}
            {group.notCompleted.length > 0 && (
              <div className="task-group">
                {group.notCompleted.map(task => renderTaskItem(task, false))}
              </div>
            )}

            {/* Выполненные задачи */}
            {hasCompleted && isCompletedExpanded && (
              <div className="task-group completed-tasks">
                {group.completed.map(task => renderTaskItem(task, true))}
              </div>
            )}

            {group.notCompleted.length === 0 && !hasCompleted && (
              <div className="empty-group">Нет задач в этой группе</div>
            )}
          </div>
        )
      })}

      {/* Модальное окно для деталей задачи */}
      {selectedTaskForDetail && (
        <TaskDetail
          taskId={selectedTaskForDetail}
          onClose={handleCloseDetail}
          onRefresh={onRefresh}
          onTaskCompleted={() => setToast({ message: 'Задача выполнена', type: 'success' })}
          onNavigate={onNavigate}
        />
      )}


      {/* Модальное окно для переноса задачи */}
      {selectedTaskForPostpone && (() => {
        const todayStr = formatDateToLocal(new Date())
        const tomorrow = new Date()
        tomorrow.setDate(tomorrow.getDate() + 1)
        const tomorrowStr = formatDateToLocal(tomorrow)
        
        // Проверяем next_show_at задачи, а не значение в поле ввода
        let nextShowAtStr = null
        if (selectedTaskForPostpone.next_show_at) {
          const nextShowAtDate = new Date(selectedTaskForPostpone.next_show_at)
          nextShowAtStr = formatDateToLocal(nextShowAtDate)
        }
        
        const isToday = nextShowAtStr === todayStr
        const isTomorrow = nextShowAtStr === tomorrowStr
        // Не показывать «Сегодня», если next_show_at уже сегодня или в прошлом
        const showTodayChip = !nextShowAtStr || nextShowAtStr > todayStr
        
        // Дата "по плану" (repetition_date / repetition_period или завтра)
        const task = selectedTaskForPostpone
        let plannedDate
        const now = new Date()
        now.setHours(0, 0, 0, 0)
        if (task.repetition_date) {
          const nextDate = calculateNextDateFromRepetitionDate(task.repetition_date)
          if (nextDate) plannedDate = nextDate
        } else if (task.repetition_period && !isZeroPeriod(task.repetition_period)) {
          const nextDate = calculateNextDateFromRepetitionPeriod(task.repetition_period)
          if (nextDate) plannedDate = nextDate
        }
        if (!plannedDate) {
          plannedDate = new Date(now)
          plannedDate.setDate(plannedDate.getDate() + 1)
        }
        plannedDate.setHours(0, 0, 0, 0)
        const plannedDateStr = formatDateToLocal(plannedDate)
        const plannedNorm = plannedDateStr.slice(0, 10)
        const nextShowNorm = nextShowAtStr ? String(nextShowAtStr).slice(0, 10) : ''
        // Показываем кнопку, если текущий next_show_at не совпадает с датой по плану
        const isCurrentDatePlanned = plannedNorm && nextShowNorm && plannedNorm === nextShowNorm
        
        const modalContent = (
          <div className="task-postpone-modal-overlay" onClick={handlePostponeClose}>
            <div className="task-postpone-modal" onClick={(e) => e.stopPropagation()}>
              <div className="task-postpone-modal-header">
                <h3>{selectedTaskForPostpone.name}</h3>
                <button onClick={handlePostponeClose} className="task-postpone-close-button">
                  ✕
                </button>
              </div>
              <div className="task-postpone-modal-content">
                <div className="task-postpone-calendar">
                  <DayPicker
                    mode="single"
                    selected={postponeDate ? new Date(postponeDate + 'T00:00:00') : undefined}
                    onSelect={handleDateSelect}
                    onDayClick={handleDayClick}
                    disabled={{ before: (() => {
                      const today = new Date()
                      today.setHours(0, 0, 0, 0)
                      return today
                    })() }}
                    locale={ru}
                  />
                </div>
                <div className="task-postpone-quick-buttons">
                  {showTodayChip && (
                    <button
                      onClick={handleTodayClick}
                      className="task-postpone-quick-button"
                      disabled={isPostponing}
                    >
                      Сегодня
                    </button>
                  )}
                  {!isTomorrow && (
                    <button
                      onClick={handleTomorrowClick}
                      className="task-postpone-quick-button"
                      disabled={isPostponing}
                    >
                      Завтра
                    </button>
                  )}
                  {!isCurrentDatePlanned && (
                    <button
                      onClick={() => handlePostponeSubmitWithDate(plannedDateStr)}
                      className="task-postpone-quick-button"
                      disabled={isPostponing}
                    >
                      По плану
                    </button>
                  )}
                  {selectedTaskForPostpone?.next_show_at && (
                    <button
                      onClick={handleWithoutDateClick}
                      className="task-postpone-quick-button"
                      disabled={isPostponing}
                    >
                      Без даты
                    </button>
                  )}
                </div>
              </div>
            </div>
          </div>
        )
        
        return typeof document !== 'undefined'
          ? createPortal(modalContent, document.body)
          : modalContent
      })()}
    </div>
  )
}

export default TaskList

