import React, { useCallback } from 'react';
import { useSetAtom, useStore } from 'jotai';
import { useTranslation } from 'react-i18next';
import { CallEmbed, useCallControlState } from '../../plugins/call';
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
import { callEmbedAtom } from '../../state/callEmbed';
import * as css from './styles.css';
import { CallActionButton } from './CallActionButton';
import {
  CallMicIcon,
  CallMicMuteIcon,
  CallPhoneDownIcon,
  CallScreenShareIcon,
  CallScreenShareMuteIcon,
  CallSpeakerIcon,
  CallSpeakerMuteIcon,
  CallVideoIcon,
  CallVideoMuteIcon,
} from './callIcons';
import { useCallSpeaker } from '../../hooks/useCallSpeaker';

// Fail-open window for widget hangup ack. If the widget is healthy, its
// im.vector.hangup / io.element.close action lands within a second and
// clearIfCurrent (CallEmbedProvider) drops the atom. If LiveKit is dead and
// the widget never replies, we fall through and force-clear locally — same
// pattern as element-web's performDisconnection TIMEOUT_MS (16s there, 8s
// here to match the mobile-ish feel of our DM flow and PEER_LEAVE_GRACE_MS).
const HANGUP_TIMEOUT_MS = 8000;

type CallControlProps = {
  callEmbed: CallEmbed;
  compact: boolean;
  callJoined: boolean;
  // native = phone app → big labelled discs; web → compact icon buttons.
  native: boolean;
};

export function CallControl({ callEmbed, compact, callJoined, native }: CallControlProps) {
  const { t } = useTranslation();
  const { microphone, video, screenshare } = useCallControlState(callEmbed.control);
  const { speaker, toggle: toggleSpeaker, available: speakerAvailable } = useCallSpeaker();
  const setCallEmbed = useSetAtom(callEmbedAtom);
  const store = useStore();

  const [hangupState, hangup] = useAsyncCallback(
    useCallback(async () => {
      // Transport.send itself can reject (widget dead / unsupported action).
      // Swallow and still poll — even a dead widget may later re-emit via the
      // Close path, and the force-clear fallback guarantees forward progress.
      try {
        await callEmbed.hangup();
      } catch {
        /* noop — fall through to poll + force-clear */
      }
      const start = Date.now();
      while (Date.now() - start < HANGUP_TIMEOUT_MS) {
        if (store.get(callEmbedAtom) !== callEmbed) return;
        // eslint-disable-next-line no-await-in-loop
        await new Promise((r) => {
          setTimeout(r, 200);
        });
      }
      if (store.get(callEmbedAtom) === callEmbed) {
        setCallEmbed(undefined);
      }
    }, [callEmbed, store, setCallEmbed])
  );
  const exiting =
    hangupState.status === AsyncStatus.Loading || hangupState.status === AsyncStatus.Success;

  const handleHangup = () => {
    if (!callJoined) {
      setCallEmbed(undefined);
      return;
    }
    hangup();
  };

  // Mic always renders. Speaker (громкая связь) only where the native route
  // plugin can act (Android). Video joins when the call isn't voice-only —
  // still shown on mobile so users can flip the camera on a phone; Screenshare
  // is desktop-only because mobile WebRTC capture is gated natively by EC.
  const showVideo = !callEmbed.voiceOnly;
  const showScreenshare = !compact && !callEmbed.voiceOnly;
  const buttonCompact = !native;

  return (
    <div className={native ? css.CallActions : css.CallActionsCompact}>
      <CallActionButton
        icon={microphone ? CallMicIcon : CallMicMuteIcon}
        label={t('Call.ctl_mic')}
        ariaLabel={microphone ? t('Call.mic_off') : t('Call.mic_on')}
        tone={microphone ? 'neutral' : 'muted'}
        disabled={!callJoined}
        compact={buttonCompact}
        onClick={() => callEmbed.control.toggleMicrophone()}
      />
      {speakerAvailable && (
        <CallActionButton
          icon={speaker ? CallSpeakerIcon : CallSpeakerMuteIcon}
          label={t('Call.ctl_speaker')}
          ariaLabel={speaker ? t('Call.speaker_off') : t('Call.speaker_on')}
          tone={speaker ? 'accent' : 'neutral'}
          disabled={!callJoined}
          compact={buttonCompact}
          onClick={toggleSpeaker}
        />
      )}
      {showVideo && (
        <CallActionButton
          icon={video ? CallVideoIcon : CallVideoMuteIcon}
          label={t('Call.ctl_camera')}
          ariaLabel={video ? t('Call.camera_off') : t('Call.camera_on')}
          tone={video ? 'accent' : 'neutral'}
          disabled={!callJoined}
          compact={buttonCompact}
          onClick={() => callEmbed.control.toggleVideo()}
        />
      )}
      {showScreenshare && (
        <CallActionButton
          icon={screenshare ? CallScreenShareIcon : CallScreenShareMuteIcon}
          label={t('Call.ctl_screen')}
          ariaLabel={screenshare ? t('Call.screenshare_off') : t('Call.screenshare_on')}
          tone={screenshare ? 'accent' : 'neutral'}
          disabled={!callJoined}
          compact={buttonCompact}
          onClick={() => callEmbed.control.toggleScreenshare()}
        />
      )}
      <CallActionButton
        icon={CallPhoneDownIcon}
        label={t('Call.ctl_end')}
        ariaLabel={t('Call.end_call')}
        tone="danger"
        busy={exiting}
        disabled={exiting}
        compact={buttonCompact}
        onClick={handleHangup}
      />
    </div>
  );
}
