import { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import { useTranslation } from 'react-i18next';

import {
  getParticipantVolume,
  setParticipantVolume,
  subscribeParticipantVolumes,
} from '../lib/participantVolumes';
import {
  AvatarColorKey,
  colorKeyFor,
} from './CallParticipantTile';
import { HeadphonesOffIcon, MicOffIcon, UsersIcon, XIcon } from './icons';

// Rows the popover knows how to render. Subset of InCallPanel's Tile so this
// component can be reused without the screen-share / video fields.
export interface ParticipantRow {
  userId: string;
  displayName: string;
  avatarUrl: string | null;
  self: boolean;
  muted: boolean;
  deafened: boolean;
}

interface Props {
  open: boolean;
  rows: ParticipantRow[];
  /** Set of userIds currently above the speaking-threshold. */
  activeSpeakers: Set<string>;
  onClose: () => void;
}

const AVATAR_TONES: Record<AvatarColorKey, string> = {
  violet: 'bg-violet-200 text-violet-800 dark:bg-violet-900/60 dark:text-violet-200',
  amber: 'bg-amber-200 text-amber-900 dark:bg-amber-900/60 dark:text-amber-200',
  rose: 'bg-rose-200 text-rose-900 dark:bg-rose-900/60 dark:text-rose-200',
  teal: 'bg-teal-200 text-teal-900 dark:bg-teal-900/60 dark:text-teal-200',
};

// Call-scoped participant list. Portal-mounted + fixed-positioned so it
// floats above whichever call layout the user is in (docked, focus, or
// fullscreen cinema). Mirrors the ParticipantVolumeMenu pattern for
// close-on-outside / close-on-Esc behaviour so both feel consistent.
export function ParticipantsPopover({ open, rows, activeSpeakers, onClose }: Props) {
  const { t } = useTranslation(['app']);

  useEffect(() => {
    if (!open) return;
    const onKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onClose();
    };
    const onDown = (e: MouseEvent) => {
      const target = e.target as HTMLElement | null;
      if (target?.closest('[data-participants-popover]')) return;
      // Clicks on the triggering button also bubble here; the button itself
      // handles toggle, so we only close on genuine outside clicks. The
      // trigger uses `data-participants-trigger` — ignore those.
      if (target?.closest('[data-participants-trigger]')) return;
      onClose();
    };
    window.addEventListener('keydown', onKey);
    window.addEventListener('mousedown', onDown);
    return () => {
      window.removeEventListener('keydown', onKey);
      window.removeEventListener('mousedown', onDown);
    };
  }, [open, onClose]);

  if (!open) return null;

  return createPortal(
    <div
      data-participants-popover
      role="dialog"
      aria-label={t('app:call.participants', { defaultValue: 'Teilnehmer' })}
      className="fixed bottom-20 right-5 z-[70] flex max-h-[60vh] w-[300px] flex-col overflow-hidden rounded-xl border border-line bg-surface-2/95 shadow-xl backdrop-blur-md"
    >
      <header className="flex items-center justify-between gap-2 border-b border-line px-3.5 py-2.5">
        <div className="flex items-center gap-2 text-sm font-semibold text-fg">
          <UsersIcon className="h-4 w-4 text-fg-muted" />
          <span>
            {t('app:call.participants', { defaultValue: 'Teilnehmer' })} · {rows.length}
          </span>
        </div>
        <button
          type="button"
          onClick={onClose}
          aria-label={t('app:common.close', { defaultValue: 'Schließen' })}
          className="flex h-7 w-7 cursor-pointer items-center justify-center rounded-md text-fg-muted hover:bg-surface-3 hover:text-fg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/50"
        >
          <XIcon className="h-4 w-4" />
        </button>
      </header>
      <div className="flex-1 overflow-y-auto px-2 py-2">
        {rows.length === 0 ? (
          <p className="px-2 py-6 text-center text-xs text-fg-muted">
            {t('app:call.no_participants', { defaultValue: 'Keine Teilnehmer.' })}
          </p>
        ) : (
          <ul className="flex flex-col gap-1">
            {rows.map((row) => (
              <li key={row.userId}>
                <Row row={row} speaking={activeSpeakers.has(row.userId)} />
              </li>
            ))}
          </ul>
        )}
      </div>
    </div>,
    document.body,
  );
}

function Row({ row, speaking }: { row: ParticipantRow; speaking: boolean }) {
  const key = colorKeyFor(row.userId);
  const tone = AVATAR_TONES[key];
  const letter = row.displayName.trim().charAt(0).toUpperCase() || '?';
  const [volume, setVolume] = useState<number>(() =>
    row.self ? 1 : getParticipantVolume(row.userId),
  );

  useEffect(() => {
    if (row.self) return;
    return subscribeParticipantVolumes(() => {
      setVolume(getParticipantVolume(row.userId));
    });
  }, [row.self, row.userId]);

  return (
    <div className="flex flex-col gap-1.5 rounded-lg px-2 py-1.5 hover:bg-surface-3/60">
      <div className="flex items-center gap-2.5">
        <div className="relative shrink-0">
          {row.avatarUrl ? (
            <img
              src={row.avatarUrl}
              alt=""
              className="h-8 w-8 rounded-full object-cover"
            />
          ) : (
            <span
              className={
                'flex h-8 w-8 items-center justify-center rounded-full text-sm font-bold ' +
                tone
              }
            >
              {letter}
            </span>
          )}
          {speaking && (
            <span
              aria-hidden="true"
              className="pointer-events-none absolute -inset-0.5 rounded-full border-2 border-emerald-500 dark:border-emerald-400"
            />
          )}
        </div>
        <div className="min-w-0 flex-1 truncate text-sm font-medium text-fg">
          {row.displayName}
          {row.self && <span className="ml-1 text-fg-muted">(du)</span>}
        </div>
        <div className="flex shrink-0 items-center gap-1">
          {row.muted && (
            <span
              aria-label="Mikro stumm"
              title="Mikro stumm"
              className="flex h-5 w-5 items-center justify-center rounded-md bg-rose-500/80 text-white"
            >
              <MicOffIcon className="h-3 w-3" />
            </span>
          )}
          {row.deafened && (
            <span
              aria-label="Ton aus"
              title="Ton aus"
              className="flex h-5 w-5 items-center justify-center rounded-md bg-rose-500/80 text-white"
            >
              <HeadphonesOffIcon className="h-3 w-3" />
            </span>
          )}
        </div>
      </div>
      {!row.self && (
        <div className="flex items-center gap-2 pl-10 text-[11px] text-fg-muted">
          <input
            type="range"
            min={0}
            max={2}
            step={0.01}
            value={volume}
            onChange={(e) => {
              const v = Number(e.target.value);
              setVolume(v);
              setParticipantVolume(row.userId, v);
            }}
            aria-label={'Lautstärke ' + row.displayName}
            className="flex-1 accent-accent"
          />
          <span
            className={
              'w-10 text-right tabular-nums ' +
              (volume > 1 ? 'text-amber-500' : '')
            }
          >
            {Math.round(volume * 100)}%
          </span>
        </div>
      )}
    </div>
  );
}
