import { Box, Icon, IconButton, IconSrc, Spinner, Text, Tooltip, TooltipProvider } from 'folds';
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 {
  CallHeadphoneIcon,
  CallHeadphoneMuteIcon,
  CallMicIcon,
  CallMicMuteIcon,
  CallPhoneDownIcon,
  CallScreenShareIcon,
  CallScreenShareMuteIcon,
  CallVideoIcon,
  CallVideoMuteIcon,
} from './callIcons';

// 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;

// All controls share the same shape so the active-call pill reads as a
// single visual family with the IncomingCallStrip Decline/Answer buttons:
// pill-radius, soft fill, size 500 (~52-56px). Variant changes with state
// (Surface = on, Warning = muted/off, Critical = hangup, Success =
// camera/screenshare on).
const BUTTON_SIZE = '500' as const;
const BUTTON_RADII = 'Pill' as const;
const BUTTON_FILL = 'Soft' as const;
const ICON_SIZE = '300' as const;

type ToggleButtonProps = {
  enabled: boolean;
  onToggle: () => void | Promise<unknown>;
  disabled?: boolean;
  iconOn: IconSrc;
  iconOff: IconSrc;
  labelOn: string;
  labelOff: string;
  variantOn?: 'Surface' | 'Success';
  variantOff?: 'Warning' | 'Surface';
};

function ToggleButton({
  enabled,
  onToggle,
  disabled,
  iconOn,
  iconOff,
  labelOn,
  labelOff,
  variantOn = 'Surface',
  variantOff = 'Warning',
}: ToggleButtonProps) {
  return (
    <TooltipProvider
      position="Top"
      tooltip={
        <Tooltip>
          <Text size="T200">{enabled ? labelOn : labelOff}</Text>
        </Tooltip>
      }
    >
      {(anchorRef) => (
        <IconButton
          ref={anchorRef}
          variant={enabled ? variantOn : variantOff}
          fill={BUTTON_FILL}
          radii={BUTTON_RADII}
          size={BUTTON_SIZE}
          onClick={() => onToggle()}
          disabled={disabled}
          aria-label={enabled ? labelOn : labelOff}
        >
          <Icon size={ICON_SIZE} src={enabled ? iconOn : iconOff} />
        </IconButton>
      )}
    </TooltipProvider>
  );
}

type CallControlProps = {
  callEmbed: CallEmbed;
  compact: boolean;
  callJoined: boolean;
};

export function CallControl({ callEmbed, compact, callJoined }: CallControlProps) {
  const { t } = useTranslation();
  const { microphone, video, sound, screenshare } = useCallControlState(callEmbed.control);
  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();
  };

  // Visibility mirrors the legacy strip: Mic + Sound always render,
  // Video joins when the call isn't voice-only (still shown on mobile —
  // users want to be able to flip the camera on a phone), Screenshare
  // is desktop-only because mobile WebRTC capture is meaningfully
  // different and Element Call gates it natively.
  const showVideo = !callEmbed.voiceOnly;
  const showScreenshare = !compact && !callEmbed.voiceOnly;

  return (
    <Box shrink="No" alignItems="Center" gap="200">
      <ToggleButton
        enabled={microphone}
        onToggle={() => callEmbed.control.toggleMicrophone()}
        disabled={!callJoined}
        iconOn={CallMicIcon}
        iconOff={CallMicMuteIcon}
        labelOn={t('Call.mic_off')}
        labelOff={t('Call.mic_on')}
      />
      <ToggleButton
        enabled={sound}
        onToggle={() => callEmbed.control.toggleSound()}
        disabled={!callJoined}
        iconOn={CallHeadphoneIcon}
        iconOff={CallHeadphoneMuteIcon}
        labelOn={t('Call.sound_off')}
        labelOff={t('Call.sound_on')}
      />
      {showVideo && (
        <ToggleButton
          enabled={video}
          onToggle={() => callEmbed.control.toggleVideo()}
          disabled={!callJoined}
          iconOn={CallVideoIcon}
          iconOff={CallVideoMuteIcon}
          labelOn={t('Call.camera_off')}
          labelOff={t('Call.camera_on')}
          variantOn="Success"
          variantOff="Surface"
        />
      )}
      {showScreenshare && (
        <ToggleButton
          enabled={screenshare}
          onToggle={() => callEmbed.control.toggleScreenshare()}
          disabled={!callJoined}
          iconOn={CallScreenShareIcon}
          iconOff={CallScreenShareMuteIcon}
          labelOn={t('Call.screenshare_off')}
          labelOff={t('Call.screenshare_on')}
          variantOn="Success"
          variantOff="Surface"
        />
      )}
      <TooltipProvider
        position="Top"
        tooltip={
          <Tooltip>
            <Text size="T200">{t('Call.end_call')}</Text>
          </Tooltip>
        }
      >
        {(anchorRef) => (
          <IconButton
            ref={anchorRef}
            variant="Critical"
            fill={BUTTON_FILL}
            radii={BUTTON_RADII}
            size={BUTTON_SIZE}
            onClick={handleHangup}
            disabled={exiting}
            aria-label={t('Call.end_call')}
          >
            {exiting ? (
              <Spinner variant="Critical" fill="Soft" size="200" />
            ) : (
              <Icon size={ICON_SIZE} src={CallPhoneDownIcon} />
            )}
          </IconButton>
        )}
      </TooltipProvider>
    </Box>
  );
}
