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

import { useAuth } from '../context/AuthContext';
import { useCall } from '../context/CallContext';
import { useConversationsContext } from '../context/ConversationsContext';
import { useFriendshipsContext } from '../context/FriendshipsContext';
import { ringtone } from '../lib/ringtone';
import { useAnyActiveCall } from '../lib/useAnyActiveCall';
import { useCallPresence } from '../lib/useCallPresence';
import {
  MicIcon,
  MicOffIcon,
  PhoneIcon,
  PhoneOffIcon,
  SpinnerIcon,
  XIcon,
} from './icons';

// Shell-level mount. Handles ringtones + the IncomingCallToast.
// The persistent CallBar (active-call widget) is rendered inside Sidebar so
// users can keep browsing/typing while a call is live.
export function CallUI() {
  const { state } = useCall();

  useEffect(() => {
    if (state.kind === 'outgoing') ringtone.start('outgoing');
    else if (state.kind === 'incoming') ringtone.start('incoming');
    else ringtone.stop();
  }, [state.kind]);

  useEffect(() => {
    return () => ringtone.stop();
  }, []);

  return <IncomingCallToast />;
}

// ---------------------------------------------------------------------------

function IncomingCallToast() {
  const { t } = useTranslation(['app']);
  const { state, acceptIncoming, rejectIncoming } = useCall();
  const { friendships } = useFriendshipsContext();
  const { conversations } = useConversationsContext();

  if (state.kind !== 'incoming') return null;

  const conv = conversations.find((c) => c.id === state.conversationId) ?? null;
  const callerName =
    conv?.members.find((m) => m.userId === state.fromUserId)?.profile?.displayName ??
    friendships.find((f) => f.peer.userId === state.fromUserId)?.peer.displayName ??
    '?';
  const isGroup = conv?.type === 'group';
  const groupName = isGroup ? (conv?.name ?? t('app:chats.new_group')) : null;
  const letter = (isGroup ? groupName ?? callerName : callerName)
    .trim()
    .charAt(0)
    .toUpperCase() || '?';

  return (
    <div
      role="dialog"
      aria-modal="false"
      aria-label={t('app:call.incoming_title')}
      className="fixed bottom-6 right-6 z-50 w-80 animate-slide-up rounded-2xl border border-white/10 bg-ink-900/95 p-5 shadow-2xl backdrop-blur-xl"
    >
      <p className="text-xs font-medium uppercase tracking-wide text-neutral-500">
        {t('app:call.incoming_title')}
      </p>
      <div className="mt-3 flex items-center gap-3">
        <div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-full bg-brand-500/30 text-base font-semibold text-white ring-1 ring-brand-400/30">
          {letter}
        </div>
        <div className="min-w-0 flex-1">
          <p className="truncate font-display text-base font-semibold text-white">
            {isGroup ? groupName : callerName}
          </p>
          <p className="truncate text-xs text-neutral-400">
            {isGroup
              ? t('app:call.incoming_group_from', {
                  name: callerName,
                  defaultValue: callerName + ' ruft Gruppe',
                })
              : t('app:call.incoming_from', { name: callerName })}
          </p>
        </div>
      </div>
      <div className="mt-4 flex gap-2">
        <button
          type="button"
          onClick={rejectIncoming}
          className="inline-flex flex-1 cursor-pointer items-center justify-center gap-2 rounded-lg border border-rose-500/30 bg-rose-500/10 px-3 py-2 text-sm font-semibold text-rose-200 transition hover:bg-rose-500/20 focus:outline-none focus-visible:ring-2 focus-visible:ring-rose-400/40"
        >
          <PhoneOffIcon className="h-4 w-4" />
          <span>{t('app:call.decline')}</span>
        </button>
        <button
          type="button"
          onClick={() => void acceptIncoming()}
          className="inline-flex flex-1 cursor-pointer items-center justify-center gap-2 rounded-lg bg-emerald-500/90 px-3 py-2 text-sm font-semibold text-white transition hover:bg-emerald-400 focus:outline-none focus-visible:ring-2 focus-visible:ring-emerald-400/40"
        >
          <PhoneIcon className="h-4 w-4" />
          <span>{t('app:call.accept')}</span>
        </button>
      </div>
    </div>
  );
}

// ---------------------------------------------------------------------------
// CallBar — persistent widget inside Sidebar. Lets the user keep browsing
// the app while a call is active / connecting / ringing.
// ---------------------------------------------------------------------------

export function CallBar() {
  const { state, lastCallConversationId } = useCall();
  const { conversations } = useConversationsContext();
  const { session } = useAuth();
  const myId = session?.user.id ?? null;

  // Aggregate observer across every conversation the user is in so a
  // "call is live, rejoin" affordance appears in the sidebar whenever ANY
  // peer is in a call — not just calls I previously joined.
  const convIds = conversations.map((c) => c.id);
  const anyActive = useAnyActiveCall(convIds, myId);

  if (state.kind === 'connected' || state.kind === 'connecting' || state.kind === 'outgoing') {
    return <ActiveCallBar />;
  }
  // Prefer the conversation I just left, fall back to any other live call.
  const rejoinId = lastCallConversationId ?? anyActive?.conversationId ?? null;
  if (rejoinId) {
    return <RejoinCallBar conversationId={rejoinId} />;
  }
  return null;
}

function ActiveCallBar() {
  const { t } = useTranslation(['app']);
  const { state, isMuted, hangup, toggleMute } = useCall();
  const { conversations } = useConversationsContext();

  const [, forceTick] = useState(0);
  useEffect(() => {
    if (state.kind !== 'connected') return;
    const id = window.setInterval(() => forceTick((v) => v + 1), 1000);
    return () => window.clearInterval(id);
  }, [state.kind]);

  if (state.kind !== 'connected' && state.kind !== 'connecting' && state.kind !== 'outgoing') {
    return null;
  }

  const conv = conversations.find((c) => c.id === state.conversationId) ?? null;
  const title =
    conv?.type === 'group'
      ? conv.name ?? '—'
      : conv?.peer?.displayName ?? '—';

  const statusLabel =
    state.kind === 'outgoing'
      ? t('app:call.outgoing_ringing')
      : state.kind === 'connecting'
        ? t('app:call.connecting')
        : t('app:call.voice_connected', { defaultValue: 'Sprachchat verbunden' });

  const elapsed =
    state.kind === 'connected'
      ? formatElapsed(Date.now() - new Date(state.startedAt).getTime())
      : null;

  return (
    <div className="mb-2 overflow-hidden rounded-xl border border-emerald-500/20 bg-ink-900/80">
      <Link
        to={'/chats/' + state.conversationId}
        className="flex items-center gap-2.5 px-3 pt-2.5 pb-2 transition hover:bg-white/5 focus:outline-none"
        aria-label={statusLabel}
      >
        <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-emerald-500/15 text-emerald-300">
          {state.kind === 'connecting' ? (
            <SpinnerIcon className="h-4 w-4" />
          ) : (
            <SignalIcon className="h-4 w-4" />
          )}
        </div>
        <div className="min-w-0 flex-1">
          <p className="truncate text-sm font-semibold text-emerald-300">{statusLabel}</p>
          <p className="truncate text-xs text-neutral-400">
            {title}
            {elapsed && <span className="ml-1.5 font-mono text-neutral-500">{elapsed}</span>}
          </p>
        </div>
      </Link>

      <div className="grid grid-cols-2 gap-1 border-t border-white/5 bg-ink-950/40 p-1.5">
        <IconTile
          onClick={toggleMute}
          disabled={state.kind !== 'connected'}
          label={isMuted ? t('app:call.unmute') : t('app:call.mute')}
          tone={isMuted ? 'amber' : 'neutral'}
        >
          {isMuted ? <MicOffIcon className="h-4 w-4" /> : <MicIcon className="h-4 w-4" />}
        </IconTile>
        <IconTile onClick={() => void hangup()} label={t('app:call.hangup')} tone="rose">
          <PhoneOffIcon className="h-4 w-4" />
        </IconTile>
      </div>
    </div>
  );
}

function RejoinCallBar({ conversationId }: { conversationId: string }) {
  const { t } = useTranslation(['app', 'common']);
  const { joinActiveCall, dismissLastCall } = useCall();
  const { conversations } = useConversationsContext();
  const { session } = useAuth();
  const active = useCallPresence(conversationId);

  const myId = session?.user.id;
  const others = active.filter((u) => u !== myId);

  // Presence polling returns [] on the first tick before it syncs, so we can't
  // treat an initial empty list as "room is empty". Only dismiss after we've
  // actually seen peers and then watched them leave — and even then confirm
  // the empty state for a grace period, since presence_diff events can
  // arrive slightly out of order.
  const [visible, setVisible] = useState(false);
  useEffect(() => {
    if (others.length > 0) {
      setVisible(true);
      return;
    }
    if (!visible) return;
    const id = window.setTimeout(() => {
      setVisible(false);
      dismissLastCall();
    }, 2500);
    return () => window.clearTimeout(id);
  }, [others.length, visible, dismissLastCall]);

  if (!visible) return null;

  const conv = conversations.find((c) => c.id === conversationId) ?? null;
  const title =
    conv?.type === 'group'
      ? conv.name ?? '—'
      : conv?.peer?.displayName ?? '—';

  return (
    <div className="mb-2 overflow-hidden rounded-xl border border-emerald-500/20 bg-ink-900/80">
      <Link
        to={'/chats/' + conversationId}
        className="flex items-center gap-2.5 px-3 pt-2.5 pb-2 transition hover:bg-white/5 focus:outline-none"
      >
        <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-emerald-500/15 text-emerald-300">
          <SignalIcon className="h-4 w-4" />
        </div>
        <div className="min-w-0 flex-1">
          <p className="truncate text-sm font-semibold text-emerald-300">
            {t('app:call.still_live', { defaultValue: 'Anruf läuft noch' })}
          </p>
          <p className="truncate text-xs text-neutral-400">
            {title} · {others.length}
          </p>
        </div>
      </Link>
      <div className="grid grid-cols-2 gap-1 border-t border-white/5 bg-ink-950/40 p-1.5">
        <IconTile
          onClick={() => void joinActiveCall(conversationId, 'audio')}
          label={t('app:call.join')}
          tone="emerald"
        >
          <PhoneIcon className="h-4 w-4" />
        </IconTile>
        <IconTile onClick={dismissLastCall} label={t('common:close', { defaultValue: 'Schließen' })} tone="neutral">
          <XIcon className="h-4 w-4" />
        </IconTile>
      </div>
    </div>
  );
}

interface IconTileProps {
  onClick: () => void;
  label: string;
  tone: 'neutral' | 'amber' | 'rose' | 'emerald';
  disabled?: boolean;
  children: React.ReactNode;
}

function IconTile({ onClick, label, tone, disabled, children }: IconTileProps) {
  const toneClass =
    tone === 'rose'
      ? 'bg-white/5 text-rose-300 hover:bg-rose-500/20 focus-visible:ring-rose-400/40'
      : tone === 'amber'
        ? 'bg-amber-500/20 text-amber-200 hover:bg-amber-500/30 focus-visible:ring-amber-400/40'
        : tone === 'emerald'
          ? 'bg-emerald-500/20 text-emerald-200 hover:bg-emerald-500/30 focus-visible:ring-emerald-400/40'
          : 'bg-white/5 text-neutral-200 hover:bg-white/10 focus-visible:ring-brand-400/40';
  return (
    <button
      type="button"
      onClick={onClick}
      disabled={disabled}
      aria-label={label}
      title={label}
      className={
        'inline-flex h-9 cursor-pointer items-center justify-center rounded-md transition focus:outline-none focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-50 ' +
        toneClass
      }
    >
      {children}
    </button>
  );
}

function SignalIcon(props: React.SVGProps<SVGSVGElement>) {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" {...props}>
      <path d="M5 12v0" />
      <path d="M9 9v6" />
      <path d="M13 6v12" />
      <path d="M17 9v6" />
    </svg>
  );
}

function formatElapsed(ms: number): string {
  const total = Math.max(0, Math.floor(ms / 1000));
  const mm = Math.floor(total / 60).toString().padStart(2, '0');
  const ss = (total % 60).toString().padStart(2, '0');
  return mm + ':' + ss;
}
