import { Hand, Mic, Pin, PinOff, ScreenShare, Video } from 'lucide-react'
import { Track } from 'livekit-client'
import {
  AudioTrack,
  ConnectionQualityIndicator,
  FocusToggle,
  LockLockedIcon,
  ParticipantName,
  ParticipantTile,
  ScreenShareIcon,
  TrackMutedIndicator,
  VideoTrack,
  isTrackReference,
  useEnsureTrackRef,
  useFeatureContext,
  useIsEncrypted,
  useParticipantInfo,
  type ParticipantClickEvent,
  type TrackReferenceOrPlaceholder,
} from '@livekit/components-react'
import { Avatar } from '@/components/ui/Avatar'
import { stageTrackKey } from '@/components/room/stageFocus'
import { parseParticipantMetadata } from '@/lib/participantMetadata'
import { useIsOrganizer } from '@/hooks/useIsOrganizer'
import { useToast } from '@/components/ui/ToastProvider'
import { muteParticipant } from '@/api/conferences'

/** Достаёт URL аватара из метаданных участника — `null`, если поля нет (гость/без аватара). */
function parseAvatarUrl(metadata: string | undefined): string | null {
  const parsed = parseParticipantMetadata(metadata)
  return typeof parsed.avatar_url === 'string' && parsed.avatar_url ? parsed.avatar_url : null
}

/**
 * Кнопки принудительного мьюта организатором (задача B2) — микрофон/камера
 * ЧУЖОГО участника. Видны только организатору (`useIsOrganizer`, подсказка
 * UI — сервер перепроверяет права по владельцу конференции в БД) и только на
 * чужой плитке камеры (на своей — обычный тулбарный toggle, мьютить себя
 * через «принудительное» действие не нужно).
 *
 * Не проверяют текущее состояние мьюта заранее (усложнило бы плитку ради
 * малополезной оптимизации): клик по уже выключенному треку — не ошибка, а
 * no-op на backend (`muted: false` в ответе, см. `services/room_control.py`).
 */
function OrganizerMuteControls({
  conferenceId,
  identity,
  displayName,
}: {
  conferenceId: string
  identity: string
  displayName: string
}) {
  const toast = useToast()

  async function handleMute(source: 'microphone' | 'camera') {
    try {
      const result = await muteParticipant(conferenceId, identity, source)
      if (!result.muted) {
        toast.show(
          source === 'microphone' ? 'Микрофон и так выключен' : 'Камера и так выключена',
          'info',
        )
      }
    } catch {
      toast.show('Не удалось выключить трек участника', 'error')
    }
  }

  return (
    <div className="room-organizer-controls">
      <button
        type="button"
        title={`Выключить микрофон: ${displayName}`}
        aria-label={`Выключить микрофон: ${displayName}`}
        onClick={(e) => {
          e.stopPropagation()
          void handleMute('microphone')
        }}
      >
        <Mic aria-hidden="true" />
      </button>
      <button
        type="button"
        title={`Выключить камеру: ${displayName}`}
        aria-label={`Выключить камеру: ${displayName}`}
        onClick={(e) => {
          e.stopPropagation()
          void handleMute('camera')
        }}
      >
        <Video aria-hidden="true" />
      </button>
    </div>
  )
}

/**
 * Содержимое плитки — рендерится ВНУТРИ `ParticipantTile` (значит, внутри её
 * `TrackRefContext`/`ParticipantContext`), поэтому берёт трек и участника из
 * контекста теми же хуками, что использует сама библиотека в оригинальной
 * разметке (см. `node_modules/@livekit/components-react/src/components/participant/ParticipantTile.tsx`,
 * версия 2.9.23 — источник этой копии).
 */
function TileBody({
  onStopSharing,
  pinnedKey,
  onTogglePin,
  raisedHandIdentities,
  conferenceId,
}: TileControlsProps) {
  const trackReference = useEnsureTrackRef()
  const isOrganizer = useIsOrganizer()
  const isEncrypted = useIsEncrypted(trackReference.participant)
  const autoManageSubscription = useFeatureContext()?.autoSubscription
  // useParticipantInfo — реактивные name/metadata участника (переподписка на
  // ParticipantMetadataChanged); в текущей версии metadata приходит статично
  // с токена при входе и в течение сеанса не меняется, но хук — штатный
  // способ читать её реактивно, если это когда-нибудь изменится.
  const { name, metadata } = useParticipantInfo({ participant: trackReference.participant })
  const avatarUrl = parseAvatarUrl(metadata)
  const displayName = name || trackReference.participant.identity
  // Чип «Вы демонстрируете экран» — только на СВОЕЙ демонстрации;
  // `onStopSharing` передаётся снаружи (`RoomStage.tsx`) только для фокус-
  // плитки, поэтому в карусели/гриде чип не появится даже при том же треке.
  const showSharingChip = Boolean(
    onStopSharing && trackReference.source === Track.Source.ScreenShare && trackReference.participant.isLocal,
  )
  // Кнопка закрепления — только там, где сцена её даёт (основное окно передаёт
  // `onTogglePin`; в мини-плеере своего тулбара нет и плитка одна, поэтому
  // булавки там нет — само закрепление, сделанное в основном окне, с 0.0.25
  // действует и в мини-плеере, см. `initialPinnedKey` в `RoomStage`).
  // Ключ плитки берём из её собственного трека: в карусели/гриде плитки
  // рендерятся шаблоном без пропсов, снаружи «какая это плитка» не передать.
  const tileKey = stageTrackKey(trackReference)
  const isPinned = pinnedKey === tileKey
  // Бейдж поднятой руки (задача B1) — только на плитке КАМЕРЫ участника, не
  // на плитке его демонстрации экрана (рука — про человека, не про экран).
  const isHandRaised =
    trackReference.source === Track.Source.Camera &&
    Boolean(raisedHandIdentities?.has(trackReference.participant.identity))
  // Кнопки принудительного мьюта (задача B2) — организатору, только на
  // чужой плитке камеры (см. докстринг `OrganizerMuteControls`).
  const showOrganizerMuteControls =
    isOrganizer &&
    Boolean(conferenceId) &&
    trackReference.source === Track.Source.Camera &&
    !trackReference.participant.isLocal

  return (
    <>
      {isTrackReference(trackReference) &&
      (trackReference.publication?.kind === 'video' ||
        trackReference.source === Track.Source.Camera ||
        trackReference.source === Track.Source.ScreenShare) ? (
        <VideoTrack trackRef={trackReference} manageSubscription={autoManageSubscription} />
      ) : (
        isTrackReference(trackReference) && <AudioTrack trackRef={trackReference} />
      )}
      {/* Вместо штатной иконки-заглушки (ParticipantPlaceholder) — аватар
          участника: фото по avatar_url из метаданных, фолбэк — инициалы имени
          (см. `ui/Avatar`). Видимость (opacity) этого блока при выключенной
          камере управляется тем же CSS-правилом библиотеки
          (`[data-lk-video-muted='true'][data-lk-source='camera'] .lk-participant-placeholder`)
          — data-атрибуты на корневой div выставляет сам `ParticipantTile`
          независимо от children. */}
      <div className="lk-participant-placeholder">
        <Avatar name={displayName} avatarUrl={avatarUrl} className="room-tile-avatar" />
      </div>
      {isHandRaised && (
        <div className="room-hand-badge" title={`${displayName}: поднята рука`}>
          <Hand className="lucide" aria-hidden="true" />
        </div>
      )}
      <div className="lk-participant-metadata">
        <div className="lk-participant-metadata-item">
          {trackReference.source === Track.Source.Camera ? (
            <>
              {isEncrypted && <LockLockedIcon style={{ marginRight: '0.25rem' }} />}
              <TrackMutedIndicator
                trackRef={{ participant: trackReference.participant, source: Track.Source.Microphone }}
                show="muted"
              />
              <ParticipantName />
            </>
          ) : (
            <>
              <ScreenShareIcon style={{ marginRight: '0.25rem' }} />
              {/* `ParticipantName` рендерит `participant.name` (или identity),
                  а `children` — необязательный суффикс ПОСЛЕ имени; оригинал
                  дописывал сюда английское `'s screen` («Имя's screen»),
                  здесь — русский суффикс («Имя — демонстрация экрана»). */}
              <ParticipantName>{' — демонстрация экрана'}</ParticipantName>
            </>
          )}
        </div>
        <ConnectionQualityIndicator className="lk-participant-metadata-item" />
      </div>
      <FocusToggle trackRef={trackReference} />
      {onTogglePin && (
        <button
          type="button"
          className={`room-pin-toggle${isPinned ? ' is-pinned' : ''}`}
          aria-pressed={isPinned}
          title={isPinned ? 'Открепить' : 'Закрепить'}
          aria-label={isPinned ? `Открепить: ${displayName}` : `Закрепить: ${displayName}`}
          onClick={(e) => {
            // Иначе клик долетит до самой плитки (`onParticipantClick`
            // у `ParticipantTile`) — булавка не должна означать «клик по плитке».
            e.stopPropagation()
            onTogglePin(tileKey)
          }}
        >
          {isPinned ? <PinOff aria-hidden="true" /> : <Pin aria-hidden="true" />}
        </button>
      )}
      {showSharingChip && (
        <div className="stage-sharing-chip">
          <ScreenShare className="lucide" aria-hidden="true" />
          <span>Вы демонстрируете экран</span>
          <button type="button" onClick={onStopSharing}>
            Остановить
          </button>
        </div>
      )}
      {showOrganizerMuteControls && conferenceId && (
        <OrganizerMuteControls
          conferenceId={conferenceId}
          identity={trackReference.participant.identity}
          displayName={displayName}
        />
      )}
    </>
  )
}

/** Управляющие элементы поверх плитки — общие для обёртки и её `TileBody`. */
interface TileControlsProps {
  /**
   * Остановить демонстрацию экрана — если передано, при рендере СВОЕЙ активной
   * демонстрации (Track.Source.ScreenShare + `participant.isLocal`) поверх
   * плитки показывается чип «Вы демонстрируете экран» с кнопкой «Остановить»
   * Передавать только для фокус-плитки — в карусели демонстрация
   * в этом приложении не появляется (см. `RoomStage.tsx`).
   */
  onStopSharing?: () => void
  /**
   * Ключ закреплённой сейчас плитки (`identity:source`, см. `stageTrackKey`) —
   * плитка сравнивает его со своим и подсвечивает булавку/рамку.
   */
  pinnedKey?: string | null
  /**
   * Закрепить/открепить эту плитку в основном окне. Передаёт свой ключ
   * (плитки в карусели/гриде рендерятся шаблоном, снаружи их не различить).
   * Не передан — кнопки-булавки на плитке нет (мини-плеер: плитка одна).
   */
  onTogglePin?: (key: string) => void
  /**
   * Identity участников с поднятой рукой прямо сейчас (задача B1, из
   * `useChat().handQueue`) — плитка сама решает, её ли это identity. Не
   * передан — бейдж нигде не рендерится (мини-плеер).
   */
  raisedHandIdentities?: Set<string>
  /**
   * Id конференции (не slug/номер) — нужен для вызова эндпоинта мьюта
   * (задача B2). Не передан — кнопок мьюта на плитке нет (мини-плеер).
   */
  conferenceId?: string
}

interface RoomParticipantTileProps extends TileControlsProps {
  trackRef?: TrackReferenceOrPlaceholder
  disableSpeakingIndicator?: boolean
  onParticipantClick?: (event: ParticipantClickEvent) => void
}

/**
 * Обёртка над штатным `ParticipantTile` (@livekit/components-react 2.9.23,
 * версия запинена в package.json — при апгрейде библиотеки сверить разметку
 * заново): та же структура (видео/аудио-трек, блок метаданных с именем и индикатором мьюта,
 * FocusToggle), но вместо стандартной SVG-пиктограммы при выключенной камере —
 * аватар участника. Работает и для локального участника (свои метаданные из
 * собственного токена), и для удалённых.
 *
 * Пин-логика оригинала (`handleSubscribe`/сброс пина при отписке от трека)
 * сознательно опущена — приложение нигде не создаёт `LayoutContext`, поэтому
 * в оригинале эта ветка и так была мёртвым кодом без провайдера контекста (по
 * той же причине ничего не рисует и штатный `FocusToggle` ниже). Своё
 * закрепление участника (задача 3.1) сделано мимо `LayoutContext`: состояние —
 * в `RoomStage`, кнопка — `.room-pin-toggle` здесь, выбор фокуса —
 * `pickStageFocus`.
 */
export function RoomParticipantTile({
  trackRef,
  disableSpeakingIndicator,
  onParticipantClick,
  onStopSharing,
  pinnedKey,
  onTogglePin,
  raisedHandIdentities,
  conferenceId,
}: RoomParticipantTileProps) {
  return (
    <ParticipantTile
      trackRef={trackRef}
      disableSpeakingIndicator={disableSpeakingIndicator}
      onParticipantClick={onParticipantClick}
    >
      <TileBody
        onStopSharing={onStopSharing}
        pinnedKey={pinnedKey}
        onTogglePin={onTogglePin}
        raisedHandIdentities={raisedHandIdentities}
        conferenceId={conferenceId}
      />
    </ParticipantTile>
  )
}
