import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react'
import { BsSearch, BsGrid3X3Gap, BsGlobe } from 'react-icons/bs'
import { api, AppMapping } from '../services/api'
import { logger } from '../utils/logger'
import { normalizeExternalUrl } from '../utils/urlUtils'
import { getAvatarPlaceholder } from '../utils/avatarUtils'
import '../index.css'

interface AppMappingListProps {
  token: string
  /** 用于隔离「最近打开」的 localStorage，与登录用户一致 */
  currentUserId: number | null
}

/** 最近打开本地存储项 */
export interface RecentAppItem {
  app_id: string
  mapped_key: string
  app_name: string
}

const RECENT_APPS_KEY_PREFIX = 'launchpad-recent-apps'
const RECENT_APPS_MAX = 5
const SHOW_CATEGORY_KEY = 'launchpad-show-category'
const OPEN_IN_SYSTEM_BROWSER_KEY = 'launchpad-open-in-system-browser'

const AppMappingList: React.FC<AppMappingListProps> = ({ token, currentUserId }) => {
  const [mappings, setMappings] = useState<AppMapping[]>([])
  const mappingsRef = useRef<AppMapping[]>([])
  const [searchKeyword, setSearchKeyword] = useState('')
  const [isLoading, setIsLoading] = useState(false)
  const [isRefreshing, setIsRefreshing] = useState(false)
  const [isLoadingMore, setIsLoadingMore] = useState(false)
  const isLoadingRef = useRef(false)
  const isLoadingMoreRef = useRef(false)
  const [hasMore, setHasMore] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [loginLoading, setLoginLoading] = useState<Record<string, boolean>>({})
  const [showCategory, setShowCategory] = useState(() => {
    try {
      const raw = localStorage.getItem(SHOW_CATEGORY_KEY)
      return raw === null ? true : raw === 'true'
    } catch {
      return true
    }
  })
  const [openInSystemBrowser, setOpenInSystemBrowser] = useState(() => {
    try {
      const raw = localStorage.getItem(OPEN_IN_SYSTEM_BROWSER_KEY)
      return raw === 'true'
    } catch {
      return false
    }
  })
  const [recentApps, setRecentApps] = useState<RecentAppItem[]>([])

  const recentStorageKey = useMemo(
    () => (currentUserId != null ? `${RECENT_APPS_KEY_PREFIX}:${currentUserId}` : null),
    [currentUserId]
  )

  // 分页状态
  const [skip, setSkip] = useState(0)
  const skipRef = useRef(0)
  const pageSize = 20
  const [total, setTotal] = useState(0)

  // 滚动容器引用
  const scrollContainerRef = useRef<HTMLDivElement>(null)
  const searchTimerRef = useRef<NodeJS.Timeout | null>(null)
  // 请求版本号，避免慢请求覆盖新搜索结果
  const loadIdRef = useRef(0)
  const searchKeywordRef = useRef(searchKeyword)
  searchKeywordRef.current = searchKeyword

  // 同步 skipRef
  useEffect(() => {
    skipRef.current = skip
  }, [skip])

  useEffect(() => {
    mappingsRef.current = mappings
  }, [mappings])

  useEffect(() => {
    isLoadingRef.current = isLoading
  }, [isLoading])
  useEffect(() => {
    isLoadingMoreRef.current = isLoadingMore
  }, [isLoadingMore])

  // 从本地加载最近打开（按用户隔离；最多 5 条，越近期越靠前）
  useEffect(() => {
    if (!recentStorageKey) {
      setRecentApps([])
      return
    }
    try {
      const raw = localStorage.getItem(recentStorageKey)
      if (raw) {
        const list = JSON.parse(raw) as RecentAppItem[]
        if (Array.isArray(list)) {
          setRecentApps(list.slice(0, RECENT_APPS_MAX))
          return
        }
      }
      setRecentApps([])
    } catch (_) {
      setRecentApps([])
    }
  }, [recentStorageKey])

  const saveRecentApp = useCallback((item: { app_id: string; mapped_key: string; app_name: string }) => {
    setRecentApps(prev => {
      const next = [
        item,
        ...prev.filter(x => !(x.app_id === item.app_id && x.mapped_key === item.mapped_key))
      ].slice(0, RECENT_APPS_MAX)
      if (recentStorageKey) {
        try {
          localStorage.setItem(recentStorageKey, JSON.stringify(next))
        } catch (_) {}
      }
      return next
    })
  }, [recentStorageKey])

  // 加载账号映射列表；silent：不清空列表、不顶全屏 loading，失败仅 warn
  const loadMappings = useCallback(
    async (isRefresh: boolean = false, options?: { silent?: boolean }) => {
      const silent = options?.silent === true

      if (isLoadingMoreRef.current && !isRefresh) return
      if (!silent && isLoadingRef.current) return

      const currentLoadId = (loadIdRef.current += 1)

      try {
        if (isRefresh) {
          setSkip(0)
          skipRef.current = 0
          if (!silent) {
            setIsRefreshing(true)
            setMappings([])
          }
        } else {
          setIsLoadingMore(true)
        }
        if (!silent) {
          setIsLoading(true)
          setError(null)
        }

        const currentSkip = isRefresh ? 0 : skipRef.current

        const keyword = searchKeywordRef.current.trim() || undefined
        const result = await api.getLaunchpadApps(token, {
          skip: currentSkip,
          limit: pageSize,
          app_name: keyword
        })

        // 仅应用最新请求的结果，避免慢请求覆盖搜索结果
        if (currentLoadId !== loadIdRef.current) return

        if (isRefresh) {
          setMappings(result.items)
          setTotal(result.total)
          setHasMore(result.items.length < result.total)
        } else {
          setMappings(prev => {
            const existingIds = new Set(prev.map(m => `${m.app_id}_${m.mapped_key}`))
            const uniqueNewMappings = result.items.filter(
              m => !existingIds.has(`${m.app_id}_${m.mapped_key}`)
            )
            const nextList = [...prev, ...uniqueNewMappings]
            setHasMore(nextList.length < result.total)
            return nextList
          })
        }

        if (isRefresh) {
          setIsRefreshing(false)
        } else {
          setIsLoadingMore(false)
        }
      } catch (err: any) {
        if (currentLoadId !== loadIdRef.current) return
        if (silent) {
          logger.warn('AppMappingList: silent refresh failed', err)
        } else {
          logger.error('AppMappingList: Failed to load mappings', err)
          setError(err.message || '加载应用列表失败')
        }
        setIsRefreshing(false)
        setIsLoadingMore(false)
      } finally {
        if (currentLoadId === loadIdRef.current) {
          if (!silent) {
            setIsLoading(false)
          }
        }
      }
    },
    [token, pageSize]
  )

  // 初始化加载
  useEffect(() => {
    loadMappings(true)
  }, [token, loadMappings])

  // 处理搜索（防抖）；已有列表时静默刷新，避免整页闪白
  useEffect(() => {
    if (searchTimerRef.current) {
      clearTimeout(searchTimerRef.current)
    }

    searchTimerRef.current = setTimeout(() => {
      loadMappings(true, { silent: mappingsRef.current.length > 0 })
    }, 500)

    return () => {
      if (searchTimerRef.current) {
        clearTimeout(searchTimerRef.current)
      }
    }
  }, [searchKeyword, loadMappings])

  // 窗口从后台切回前台时静默刷新
  useEffect(() => {
    const onVis = () => {
      if (document.visibilityState === 'visible' && token) {
        void loadMappings(true, { silent: true })
      }
    }
    document.addEventListener('visibilitychange', onVis)
    return () => document.removeEventListener('visibilitychange', onVis)
  }, [token, loadMappings])

  // 处理滚动事件（使用节流避免频繁触发）
  const scrollTimerRef = useRef<NodeJS.Timeout | null>(null)
  const handleScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
    const container = e.currentTarget
    if (!container) return

    const { scrollTop, scrollHeight, clientHeight } = container

    // 节流处理加载逻辑
    if (scrollTimerRef.current) {
      clearTimeout(scrollTimerRef.current)
    }

    scrollTimerRef.current = setTimeout(() => {
      // 下拉刷新：滚动到顶部附近（往上拉）
      if (scrollTop < 50 && !isRefreshing && !isLoading) {
        loadMappings(true)
      }

      // 上拉加载更多：滚动到底部附近（往下拉）
      const distanceToBottom = scrollHeight - scrollTop - clientHeight
      if (distanceToBottom < 100 && hasMore && !isLoadingMore && !isLoading) {
        const nextSkip = skipRef.current + pageSize
        setSkip(nextSkip)
        skipRef.current = nextSkip
        loadMappings(false)
      }
    }, 100)
  }, [isRefreshing, isLoading, isLoadingMore, hasMore, loadMappings])

  // 前端按关键词过滤展示列表（兜底：后端未过滤时也能正确显示）
  const displayedMappings = useMemo(() => {
    const kw = searchKeyword.trim().toLowerCase()
    if (!kw) return mappings
    return mappings.filter(m => (m.app_name || '').toLowerCase().includes(kw))
  }, [mappings, searchKeyword])

  const categorizedApps = useMemo(() => {
    const groupMap = new Map<number | null, { categoryId: number | null; categoryName: string; apps: AppMapping[] }>()

    for (const app of displayedMappings) {
      const key = app.category_id
      if (!groupMap.has(key)) {
        groupMap.set(key, {
          categoryId: key,
          categoryName: key === null ? '未分类' : (app.category_name || '未分类'),
          apps: [],
        })
      }
      groupMap.get(key)!.apps.push(app)
    }

    const groups = Array.from(groupMap.values())
    groups.sort((a, b) => {
      if (a.categoryId === null && b.categoryId !== null) return 1
      if (a.categoryId !== null && b.categoryId === null) return -1
      return a.categoryName.localeCompare(b.categoryName, 'zh-CN')
    })

    return groups
  }, [displayedMappings])

  // 处理 SSO 登录
  const handleSsoLogin = async (mapping: AppMapping) => {
    if (!mapping.is_active) {
      alert('该账号已禁用，无法登录')
      return
    }

    setLoginLoading(prev => ({ ...prev, [mapping.app_id]: true }))
    
    try {
      const result = await api.ssoLogin(token, mapping.app_id)
      
      if (result.redirect_url) {
        const normalized = normalizeExternalUrl(result.redirect_url)
        if (!normalized) {
          alert('无效的跳转地址')
          return
        }
        if (openInSystemBrowser && window.electron?.ipcRenderer) {
          window.electron.ipcRenderer.send('open-url-external', normalized)
        } else {
          window.open(normalized, '_blank')
        }
        saveRecentApp({ app_id: mapping.app_id, mapped_key: mapping.mapped_key, app_name: mapping.app_name })
      } else {
        alert('SSO 登录失败：未返回跳转地址')
      }
    } catch (err: any) {
      logger.error('AppMappingList: SSO login failed', err)
      alert(err.message || 'SSO 登录失败')
    } finally {
      setLoginLoading(prev => ({ ...prev, [mapping.app_id]: false }))
    }
  }

  // 点击最近打开项：优先用当前列表中的 mapping，否则用本地记录发起 SSO
  const handleRecentAppClick = useCallback(async (item: RecentAppItem) => {
    const mapping = mappings.find(m => m.app_id === item.app_id && m.mapped_key === item.mapped_key)
    if (mapping) {
      if (!mapping.is_active) return
      await handleSsoLogin(mapping)
      return
    }
    setLoginLoading(prev => ({ ...prev, [item.app_id]: true }))
    try {
      const result = await api.ssoLogin(token, item.app_id)
      if (result.redirect_url) {
        const normalized = normalizeExternalUrl(result.redirect_url)
        if (!normalized) {
          alert('无效的跳转地址')
          return
        }
        if (openInSystemBrowser && window.electron?.ipcRenderer) {
          window.electron.ipcRenderer.send('open-url-external', normalized)
        } else {
          window.open(normalized, '_blank')
        }
        saveRecentApp(item)
      } else {
        alert('SSO 登录失败：未返回跳转地址')
      }
    } catch (err: any) {
      logger.error('AppMappingList: SSO login failed (recent)', err)
      alert((err as Error).message || 'SSO 登录失败')
    } finally {
      setLoginLoading(prev => ({ ...prev, [item.app_id]: false }))
    }
  }, [token, mappings, saveRecentApp, openInSystemBrowser])

  return (
    <div style={{ flex: 1, padding: '40px', overflowY: 'auto' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '30px' }}>
        <h2 style={{ margin: 0, color: '#333', WebkitAppRegion: 'drag' } as React.CSSProperties}>应用中心</h2>
        <div style={{ display: 'flex', alignItems: 'center', gap: '8px', WebkitAppRegion: 'no-drag' } as React.CSSProperties}>
          <button
            title={openInSystemBrowser ? '在应用内打开' : '用系统浏览器打开'}
            onClick={() => {
              setOpenInSystemBrowser(prev => {
                const next = !prev
                try {
                  localStorage.setItem(OPEN_IN_SYSTEM_BROWSER_KEY, next ? 'true' : 'false')
                } catch (_) {}
                return next
              })
            }}
            style={{
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
              width: '36px',
              height: '36px',
              padding: 0,
              borderRadius: '6px',
              border: '1px solid #e0e0e0',
              backgroundColor: openInSystemBrowser ? '#1890ff' : '#f5f5f5',
              color: openInSystemBrowser ? '#fff' : '#666',
              cursor: 'pointer',
              fontSize: '16px',
              transition: 'all 0.2s',
            } as React.CSSProperties}
          >
            <BsGlobe style={{ fontSize: '18px' }} />
          </button>
          <button
            onClick={() => {
              setShowCategory(prev => {
                const next = !prev
                try {
                  localStorage.setItem(SHOW_CATEGORY_KEY, JSON.stringify(next))
                } catch (_) {}
                return next
              })
            }}
            style={{
              display: 'flex',
              alignItems: 'center',
              gap: '6px',
              padding: '6px 14px',
              borderRadius: '6px',
              border: '1px solid #e0e0e0',
              backgroundColor: showCategory ? '#1890ff' : '#f5f5f5',
              color: showCategory ? '#fff' : '#666',
              cursor: 'pointer',
              fontSize: '13px',
              transition: 'all 0.2s',
            } as React.CSSProperties}
          >
            <BsGrid3X3Gap style={{ fontSize: '14px' }} />
            {showCategory ? '取消分类' : '显示分类'}
          </button>
        </div>
      </div>
      
      {/* 搜索框 */}
      <div style={{ marginBottom: '20px' }}>
        <div style={{ 
          position: 'relative', 
          display: 'flex', 
          alignItems: 'center',
          backgroundColor: '#f5f5f5',
          borderRadius: '8px',
          padding: '10px 12px',
          border: '1px solid #e0e0e0'
        }}>
          <BsSearch style={{ color: '#999', marginRight: '8px', fontSize: '16px' }} />
          <input
            type="text"
            placeholder="搜索应用名称"
            value={searchKeyword}
            onChange={(e) => setSearchKeyword(e.target.value)}
            style={{
              flex: 1,
              border: 'none',
              outline: 'none',
              fontSize: '14px',
              color: '#333',
              background: 'transparent'
            }}
          />
          {searchKeyword && (
            <button
              onClick={() => setSearchKeyword('')}
              style={{
                background: 'none',
                border: 'none',
                color: '#999',
                cursor: 'pointer',
                fontSize: '20px',
                padding: 0,
                width: '24px',
                height: '24px',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center'
              }}
            >
              ×
            </button>
          )}
        </div>
      </div>

      {/* 最近打开：仅显示 5 条，与全部应用用分隔线隔开 */}
      {recentApps.length > 0 && (
        <div style={{ marginBottom: '24px', paddingBottom: '24px', borderBottom: '1px solid #eee' }}>
          <div style={{
            fontSize: '15px',
            fontWeight: 'bold',
            color: '#333',
            marginBottom: '12px',
            paddingBottom: '8px',
            borderBottom: '1px solid #eee',
            display: 'flex',
            alignItems: 'center',
            gap: '8px',
          }}>
            最近打开
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, minmax(0, 1fr))', gap: '12px' }}>
            {recentApps.map((item) => (
              <div
                key={`${item.app_id}_${item.mapped_key}`}
                className={`browser-card browser-card--recent ${loginLoading[item.app_id] ? 'disabled' : ''}`}
                onClick={() => !loginLoading[item.app_id] && handleRecentAppClick(item)}
                style={{
                  opacity: loginLoading[item.app_id] ? 0.6 : 1,
                  cursor: loginLoading[item.app_id] ? 'not-allowed' : 'pointer',
                  WebkitAppRegion: 'no-drag',
                } as React.CSSProperties}
              >
                {getAvatarPlaceholder(item.app_name, item.app_id, 28, { marginRight: 10 }, 'applicationIcon', 'roundedSquare')}
                <div style={{ minWidth: 0 }}>
                  <div style={{ fontWeight: 'bold', fontSize: '13px' }}>{item.app_name}</div>
                  {loginLoading[item.app_id] && (
                    <div style={{ fontSize: '11px', color: '#888', marginTop: '2px' }}>登录中...</div>
                  )}
                </div>
              </div>
            ))}
          </div>
        </div>
      )}

      {/* 应用列表 */}
      <div
        ref={scrollContainerRef}
        onScroll={handleScroll}
        style={{ WebkitAppRegion: 'no-drag' } as React.CSSProperties}
      >
        {/* 下拉刷新提示 */}
        {isRefreshing && (
          <div style={{ textAlign: 'center', padding: '20px', color: '#999' }}>
            正在刷新...
          </div>
        )}

        {/* 错误提示 */}
        {error && (
          <div style={{ textAlign: 'center', padding: '20px', color: '#f44336' }}>
            {error}
          </div>
        )}

        {showCategory ? (
          categorizedApps.map((group) => (
            <div key={group.categoryId ?? 'uncategorized'} style={{ marginBottom: '28px' }}>
              <div style={{
                fontSize: '15px',
                fontWeight: 'bold',
                color: '#333',
                marginBottom: '12px',
                paddingBottom: '8px',
                borderBottom: '1px solid #eee',
                display: 'flex',
                alignItems: 'center',
                gap: '8px',
              }}>
                {group.categoryName}
                <span style={{ fontSize: '12px', color: '#999', fontWeight: '400' }}>
                  ({group.apps.length})
                </span>
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(240px, 1fr))', gap: '20px' }}>
                {group.apps.map((mapping) => (
                  <div
                    key={`${mapping.app_id}_${mapping.mapped_key}`}
                    className={`browser-card ${!mapping.is_active || loginLoading[mapping.app_id] ? 'disabled' : ''}`}
                    onClick={() => {
                      if (mapping.is_active && (mapping.protocol_type === 'SIMPLE_API' || mapping.protocol_type === 'OIDC') && !loginLoading[mapping.app_id]) {
                        handleSsoLogin(mapping)
                      }
                    }}
                    style={{
                      opacity: !mapping.is_active || loginLoading[mapping.app_id] ? 0.6 : 1,
                      cursor: !mapping.is_active || loginLoading[mapping.app_id] ? 'not-allowed' : 'pointer'
                    }}
                  >
                    {getAvatarPlaceholder(mapping.app_name, mapping.app_id, 40, { marginRight: 15 }, 'applicationIcon', 'roundedSquare')}
                    <div>
                      <div style={{ fontWeight: 'bold' }}>{mapping.app_name}</div>
                      {loginLoading[mapping.app_id] && (
                        <div style={{ fontSize: '12px', color: '#888', marginTop: '4px' }}>登录中...</div>
                      )}
                    </div>
                  </div>
                ))}
              </div>
            </div>
          ))
        ) : (
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(240px, 1fr))', gap: '20px' }}>
            {displayedMappings.map((mapping) => (
              <div
                key={`${mapping.app_id}_${mapping.mapped_key}`}
                className={`browser-card ${!mapping.is_active || loginLoading[mapping.app_id] ? 'disabled' : ''}`}
                onClick={() => {
                  if (mapping.is_active && (mapping.protocol_type === 'SIMPLE_API' || mapping.protocol_type === 'OIDC') && !loginLoading[mapping.app_id]) {
                    handleSsoLogin(mapping)
                  }
                }}
                style={{
                  opacity: !mapping.is_active || loginLoading[mapping.app_id] ? 0.6 : 1,
                  cursor: !mapping.is_active || loginLoading[mapping.app_id] ? 'not-allowed' : 'pointer'
                }}
              >
                {getAvatarPlaceholder(mapping.app_name, mapping.app_id, 40, { marginRight: 15 }, 'applicationIcon', 'roundedSquare')}
                <div>
                  <div style={{ fontWeight: 'bold' }}>{mapping.app_name}</div>
                  {loginLoading[mapping.app_id] && (
                    <div style={{ fontSize: '12px', color: '#888', marginTop: '4px' }}>登录中...</div>
                  )}
                </div>
              </div>
            ))}
          </div>
        )}

        {/* 加载更多提示 */}
        {isLoadingMore && (
          <div style={{ textAlign: 'center', padding: '20px', color: '#999' }}>
            加载中...
          </div>
        )}

        {/* 没有更多数据提示 */}
        {!hasMore && displayedMappings.length > 0 && (
          <div style={{ textAlign: 'center', padding: '20px', color: '#999' }}>
            没有更多了
          </div>
        )}

        {/* 空状态 */}
        {!isLoading && displayedMappings.length === 0 && !error && (
          <div style={{ textAlign: 'center', padding: '60px 20px', color: '#999' }}>
            <div style={{ fontSize: '64px', marginBottom: '16px', opacity: 0.5 }}>📱</div>
            <div style={{ fontSize: '16px' }}>{searchKeyword.trim() ? '未找到匹配的应用' : '暂无应用'}</div>
          </div>
        )}
      </div>
    </div>
  )
}

export default AppMappingList
