import { useEffect, useRef, useState } from 'react'
import { Room, RoomEvent, Track } from 'livekit-client'
import './App.css'

type Role = 'host' | 'publisher' | 'viewer'

type User = {
  id: string
  username: string
  displayName: string
  isAdmin: boolean
}

type Session = { token: string; user: User }

type TokenResponse = {
  roomName: string
  participantIdentity: string
  participantName: string
  livekitUrl: string
  token: string
  role: Role
}

type ScreenFeed = { track: Track; label: string }
type RoomHistoryItem = { id: string; title: string; visitedAt: string }

const ROOM_HISTORY_KEY = 'screen-share-room-history'

function ScreenTrack({ track, label }: ScreenFeed) {
  const mediaContainerRef = useRef<HTMLDivElement>(null)

  useEffect(() => {
    const mediaElement = track.attach()
    mediaElement.autoplay = true
    if (mediaElement instanceof HTMLVideoElement) {
      mediaElement.muted = true
      mediaElement.playsInline = true
      void mediaElement.play().catch(() => undefined)
    }
    mediaContainerRef.current?.append(mediaElement)
    return () => mediaElement.remove()
  }, [track])

  return (
    <figure className="screen-frame">
      <figcaption>{label}</figcaption>
      <div className="media-slot" ref={mediaContainerRef} />
    </figure>
  )
}

async function apiRequest<T>(path: string, options: RequestInit = {}, token?: string): Promise<T> {
  const response = await fetch(path, {
    ...options,
    headers: {
      'Content-Type': 'application/json',
      ...(token ? { Authorization: 'Bearer ' + token } : {}),
      ...options.headers,
    },
  })
  const payload = (await response.json()) as T | { message: string }
  if (!response.ok) {
    const message = typeof payload === 'object' && payload !== null && 'message' in payload
      ? String(payload.message)
      : '请求失败'
    throw new Error(message)
  }
  return payload as T
}

function App() {
  const roomRef = useRef<Room | null>(null)
  const [session, setSession] = useState<Session | null>(null)
  const [authMode, setAuthMode] = useState<'login' | 'register'>('login')
  const [username, setUsername] = useState('')
  const [password, setPassword] = useState('')
  const [displayName, setDisplayName] = useState('')
  const [inviteCode, setInviteCode] = useState('')
  const [roomId, setRoomId] = useState('')
  const [roomTitle, setRoomTitle] = useState('')
  const [roomHistory, setRoomHistory] = useState<RoomHistoryItem[]>(() => {
    try {
      const stored = localStorage.getItem(ROOM_HISTORY_KEY)
      return stored ? JSON.parse(stored) as RoomHistoryItem[] : []
    } catch {
      return []
    }
  })
  const [connectionState, setConnectionState] = useState<'idle' | 'connecting' | 'connected'>('idle')
  const [roomRole, setRoomRole] = useState<Role | null>(null)
  const [isSharing, setIsSharing] = useState(false)
  const [remoteCount, setRemoteCount] = useState(0)
  const [localScreen, setLocalScreen] = useState<ScreenFeed | null>(null)
  const [remoteScreens, setRemoteScreens] = useState<ScreenFeed[]>([])
  const [createdInvite, setCreatedInvite] = useState('')
  const [error, setError] = useState('')

  useEffect(() => {
    const stored = localStorage.getItem('screen-share-session')
    if (!stored) return
    const saved = JSON.parse(stored) as Session
    void apiRequest<User>('/api/auth/me', {}, saved.token)
      .then((user) => setSession({ token: saved.token, user }))
      .catch(() => localStorage.removeItem('screen-share-session'))
  }, [])

  useEffect(() => () => {
    void roomRef.current?.disconnect()
  }, [])

  function saveSession(next: Session) {
    localStorage.setItem('screen-share-session', JSON.stringify(next))
    setSession(next)
  }

  function refreshRemoteCount(room: Room) {
    setRemoteCount(room.remoteParticipants.size)
  }

  function rememberRoom(room: Pick<RoomHistoryItem, 'id' | 'title'>) {
    setRoomHistory((history) => {
      const next = [
        { ...room, visitedAt: new Date().toISOString() },
        ...history.filter((item) => item.id !== room.id),
      ].slice(0, 12)
      localStorage.setItem(ROOM_HISTORY_KEY, JSON.stringify(next))
      return next
    })
  }

  async function submitAuth() {
    setError('')
    try {
      const body = authMode === 'login'
        ? { username, password }
        : { username, password, displayName, inviteCode }
      const result = await apiRequest<Session>('/api/auth/' + authMode, {
        method: 'POST',
        body: JSON.stringify(body),
      })
      saveSession(result)
      setPassword('')
      setInviteCode('')
    } catch (cause) {
      setError(cause instanceof Error ? cause.message : '认证失败')
    }
  }

  async function createInvite(type: 'user' | 'admin') {
    if (!session) return
    setError('')
    try {
      const result = await apiRequest<{ code: string }>('/api/admin/invitations', {
        method: 'POST',
        body: JSON.stringify({ type, expiresInHours: 168 }),
      }, session.token)
      setCreatedInvite(result.code)
    } catch (cause) {
      setError(cause instanceof Error ? cause.message : '创建邀请码失败')
    }
  }

  async function createRoom() {
    if (!session || !roomTitle.trim()) {
      setError('请输入房间标题。')
      return
    }
    setError('')
    try {
      const room = await apiRequest<{ id: string; title: string }>('/api/rooms', {
        method: 'POST',
        body: JSON.stringify({ title: roomTitle.trim() }),
      }, session.token)
      setRoomId(room.id)
      setRoomTitle(room.title)
      rememberRoom(room)
      await connectRoom(room.id)
    } catch (cause) {
      setError(cause instanceof Error ? cause.message : '创建房间失败')
    }
  }

  async function joinRoomByTitle() {
    if (!session || !roomTitle.trim()) {
      setError('请输入房间标题。')
      return
    }
    setError('')
    try {
      const room = await apiRequest<{ id: string; title: string }>('/api/rooms/join-by-title', {
        method: 'POST',
        body: JSON.stringify({ title: roomTitle.trim() }),
      }, session.token)
      setRoomId(room.id)
      setRoomTitle(room.title)
      rememberRoom(room)
      await connectRoom(room.id)
    } catch (cause) {
      setError(cause instanceof Error ? cause.message : '加入房间失败')
    }
  }

  async function connectRoom(targetRoomId = roomId.trim()) {
    if (!session) return
    if (!targetRoomId) {
      setError('请输入房间 ID。')
      return
    }
    setError('')
    setConnectionState('connecting')

    try {
      await apiRequest('/api/rooms/' + encodeURIComponent(targetRoomId) + '/join', {
        method: 'POST',
        body: '{}',
      }, session.token)
      const payload = await apiRequest<TokenResponse>('/api/rooms/' + encodeURIComponent(targetRoomId) + '/token', {
        method: 'POST',
        body: '{}',
      }, session.token)

      roomRef.current?.disconnect()
      setLocalScreen(null)
      setRemoteScreens([])
      const room = new Room()
      room.on(RoomEvent.ParticipantConnected, () => refreshRemoteCount(room))
      room.on(RoomEvent.ParticipantDisconnected, () => refreshRemoteCount(room))
      room.on(RoomEvent.TrackSubscribed, (track, publication, participant) => {
        if (track.kind === Track.Kind.Video && publication.source === Track.Source.ScreenShare) {
          setRemoteScreens((screens) => [
            ...screens.filter((screen) => screen.track.sid !== track.sid),
            { track, label: (participant.name || participant.identity) + ' 的屏幕' },
          ])
        }
      })
      room.on(RoomEvent.TrackUnsubscribed, (track) => {
        setRemoteScreens((screens) => screens.filter((screen) => screen.track !== track))
      })
      room.on(RoomEvent.LocalTrackPublished, (publication) => {
        if (publication.source === Track.Source.ScreenShare && publication.track) {
          setLocalScreen({ track: publication.track, label: '你正在共享的屏幕' })
          setIsSharing(true)
        }
      })
      room.on(RoomEvent.LocalTrackUnpublished, (publication) => {
        if (publication.source === Track.Source.ScreenShare) {
          setLocalScreen(null)
          setIsSharing(false)
        }
      })
      room.on(RoomEvent.Disconnected, () => {
        setConnectionState('idle')
        setRoomRole(null)
        setIsSharing(false)
        setRemoteCount(0)
        setLocalScreen(null)
        setRemoteScreens([])
      })

      await room.connect(payload.livekitUrl, payload.token)
      roomRef.current = room
      setRoomId(targetRoomId)
      setRoomRole(payload.role)
      refreshRemoteCount(room)
      setConnectionState('connected')
    } catch (cause) {
      setConnectionState('idle')
      setError(cause instanceof Error ? cause.message : '连接房间失败')
    }
  }

  async function toggleScreenShare() {
    if (!roomRef.current) return
    try {
      await roomRef.current.localParticipant.setScreenShareEnabled(!isSharing)
    } catch (cause) {
      setError(cause instanceof Error ? cause.message : '无法切换屏幕共享')
    }
  }

  function leaveRoom() {
    roomRef.current?.disconnect()
    roomRef.current = null
  }

  function logout() {
    leaveRoom()
    localStorage.removeItem('screen-share-session')
    setSession(null)
    setCreatedInvite('')
  }

  const connected = connectionState === 'connected'
  const canShare = roomRole === 'host' || roomRole === 'publisher'

  if (!session) {
    return (
      <main className="app-shell narrow">
        <header>
          <p className="eyebrow">SCREEN SHARE</p>
          <h1>{authMode === 'login' ? '登录' : '使用邀请码注册'}</h1>
        </header>
        <section className="auth-card">
          <label>用户名<input value={username} onChange={(event) => setUsername(event.target.value)} /></label>
          <label>密码<input type="password" value={password} onChange={(event) => setPassword(event.target.value)} /></label>
          {authMode === 'register' && (
            <>
              <label>显示名称<input value={displayName} onChange={(event) => setDisplayName(event.target.value)} /></label>
              <label>邀请码<input value={inviteCode} onChange={(event) => setInviteCode(event.target.value)} /></label>
            </>
          )}
          <button type="button" className="primary" onClick={() => void submitAuth()}>
            {authMode === 'login' ? '登录' : '注册并登录'}
          </button>
          <button type="button" className="link-button" onClick={() => setAuthMode(authMode === 'login' ? 'register' : 'login')}>
            {authMode === 'login' ? '没有账号？使用邀请码注册' : '已有账号？去登录'}
          </button>
          {error && <p className="error" role="alert">{error}</p>}
        </section>
      </main>
    )
  }

  return (
    <main className="app-shell">
      <header className="app-header">
        <div>
          <p className="eyebrow">LIVEKIT · 已登录</p>
          <h1>屏幕共享房间</h1>
          <p className="subtitle">当前用户：{session.user.displayName}（{session.user.isAdmin ? '管理员' : '普通用户'}）</p>
        </div>
        <button type="button" className="secondary" onClick={logout}>退出登录</button>
      </header>

      {session.user.isAdmin && (
        <section className="admin-panel">
          <strong>管理员邀请码</strong>
          <button type="button" onClick={() => void createInvite('user')}>创建普通用户邀请码</button>
          <button type="button" onClick={() => void createInvite('admin')}>创建管理员邀请码</button>
          {createdInvite && <code className="invite-code">{createdInvite}</code>}
        </section>
      )}

      <section className="join-panel room-panel">
        <label>房间标题<input list="room-title-history" value={roomTitle} onChange={(event) => setRoomTitle(event.target.value)} disabled={connected} placeholder="输入标题以创建或加入" />
          <datalist id="room-title-history">{roomHistory.map((item) => <option key={item.id} value={item.title} />)}</datalist>
        </label>
        <button type="button" className="primary" onClick={() => void createRoom()} disabled={connected}>创建房间</button>
        {!connected
          ? <button type="button" className="secondary" onClick={() => void joinRoomByTitle()} disabled={connectionState === 'connecting'}>{connectionState === 'connecting' ? '正在连接…' : '按标题加入'}</button>
          : <button type="button" className="secondary" onClick={leaveRoom}>离开房间</button>}
        <label className="advanced-field">房间 ID（备用）<input value={roomId} onChange={(event) => setRoomId(event.target.value)} disabled={connected} placeholder="UUID" /></label>
        {!connected && <button type="button" className="secondary id-join" onClick={() => void connectRoom()} disabled={connectionState === 'connecting'}>使用 ID 加入</button>}
      </section>

      {roomHistory.length > 0 && (
        <section className="room-history">
          <strong>最近房间</strong>
          {roomHistory.map((item) => (
            <button key={item.id} type="button" onClick={() => {
              setRoomTitle(item.title)
              setRoomId(item.id)
              void connectRoom(item.id)
            }} disabled={connected}>{item.title}</button>
          ))}
        </section>
      )}

      {error && <p className="error" role="alert">{error}</p>}

      <section className="status-bar" aria-live="polite">
        <span className={connected ? 'status online' : 'status'}>{connected ? '已连接' : '未连接'}</span>
        <span>{roomRole ? '房间角色：' + roomRole : '尚未加入房间'}</span>
        <span>其他参与者：{remoteCount}</span>
        {connected && <button type="button" className="share-button" onClick={() => void toggleScreenShare()} disabled={!canShare}>{isSharing ? '停止共享屏幕' : '共享屏幕'}</button>}
      </section>

      <section className="screens">
        <div className="screen-section"><h2>我的屏幕</h2><div className="video-stack">{localScreen ? <ScreenTrack {...localScreen} /> : <p className="empty">尚未共享屏幕</p>}</div></div>
        <div className="screen-section"><h2>远端屏幕</h2><div className="video-stack">{remoteScreens.length === 0 ? <p className="empty">等待其他参与者共享屏幕</p> : remoteScreens.map((screen) => <ScreenTrack key={screen.track.sid} {...screen} />)}</div></div>
      </section>
    </main>
  )
}

export default App
