import React, { MouseEventHandler, useCallback, useRef, useState } from 'react';
import {
  Box,
  Chip,
  Icon,
  IconButton,
  Icons,
  Menu,
  PopOut,
  RectCords,
  Spinner,
  Text,
  Tooltip,
  TooltipProvider,
  color,
  config,
} from 'folds';
import FocusTrap from 'focus-trap-react';
import { CallEmbed } from '../../plugins/call';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { useSoundboard } from '../../hooks/useSoundboard';
import { useSetting } from '../../state/hooks/settings';
import { settingsAtom } from '../../state/settings';
import { stopPropagation } from '../../utils/keyboard';
import {
  SOUNDBOARD_ACCEPT,
  SOUNDBOARD_MAX_CLIPS,
  playClipLocally,
  resolveClipObjectUrl,
} from '../../utils/soundboardClips';

type CallSoundboardProps = {
  callEmbed: CallEmbed;
};

/**
 * [P5-15] In-call soundboard: trigger user-uploaded clips into the call. Each
 * clip is published to peers as a separate track by the EC fork
 * (`io.lotus.inject_audio`) and also played locally for the presser's feedback.
 * Clips are uploadable/managed here and synced across devices via the
 * `io.lotus.soundboard` account data (like custom emoji/sticker packs).
 */
export function CallSoundboard({ callEmbed }: CallSoundboardProps) {
  const mx = useMatrixClient();
  const { clips, addClip, removeClip } = useSoundboard();
  const [soundboardVolume] = useSetting(settingsAtom, 'soundboardVolume');

  const [cords, setCords] = useState<RectCords>();
  const [busyId, setBusyId] = useState<string>();
  const [uploading, setUploading] = useState(false);
  const [error, setError] = useState<string>();
  const fileInputRef = useRef<HTMLInputElement>(null);

  const volume = Math.max(0, Math.min(1, soundboardVolume / 100));

  const handleOpen: MouseEventHandler<HTMLButtonElement> = (evt) => {
    setError(undefined);
    setCords(evt.currentTarget.getBoundingClientRect());
  };

  const handlePlay = useCallback(
    async (id: string, mxc: string) => {
      setBusyId(id);
      setError(undefined);
      try {
        const objectUrl = await resolveClipObjectUrl(mx, mxc);
        callEmbed.control.injectAudio(objectUrl, volume);
        playClipLocally(objectUrl, volume);
      } catch {
        setError('Could not play that clip.');
      } finally {
        setBusyId(undefined);
      }
    },
    [mx, callEmbed, volume],
  );

  const handleFile = useCallback(
    async (file: File | undefined) => {
      if (!file) return;
      setUploading(true);
      setError(undefined);
      try {
        await addClip(file);
      } catch (e) {
        setError(e instanceof Error ? e.message : 'Upload failed.');
      } finally {
        setUploading(false);
      }
    },
    [addClip],
  );

  return (
    <>
      <input
        ref={fileInputRef}
        type="file"
        accept={SOUNDBOARD_ACCEPT}
        hidden
        onChange={(e) => {
          handleFile(e.target.files?.[0]);
          e.target.value = '';
        }}
      />
      <PopOut
        anchor={cords}
        position="Top"
        align="Center"
        content={
          <FocusTrap
            focusTrapOptions={{
              initialFocus: false,
              onDeactivate: () => setCords(undefined),
              clickOutsideDeactivates: true,
              escapeDeactivates: stopPropagation,
            }}
          >
            <Menu style={{ maxWidth: '320px' }}>
              <Box direction="Column" gap="200" style={{ padding: config.space.S200 }}>
                <Box alignItems="Center" justifyContent="SpaceBetween" gap="200">
                  <Text size="L400">Soundboard</Text>
                  <Chip
                    variant="Secondary"
                    radii="Pill"
                    disabled={uploading || clips.length >= SOUNDBOARD_MAX_CLIPS}
                    onClick={() => fileInputRef.current?.click()}
                    before={
                      uploading ? <Spinner size="100" /> : <Icon size="100" src={Icons.Plus} />
                    }
                  >
                    <Text size="B300">Upload</Text>
                  </Chip>
                </Box>

                {clips.length === 0 ? (
                  <Text size="T200" priority="300">
                    No clips yet. Upload a short audio clip (max 1 MB) to play it into the call.
                    Clips sync across your devices.
                  </Text>
                ) : (
                  <Box wrap="Wrap" gap="200">
                    {clips.map((clip) => (
                      <Box
                        key={clip.id}
                        direction="Column"
                        gap="100"
                        style={{ position: 'relative' }}
                      >
                        <Chip
                          variant="SurfaceVariant"
                          radii="300"
                          disabled={busyId === clip.id}
                          onClick={() => handlePlay(clip.id, clip.url)}
                          before={
                            busyId === clip.id ? (
                              <Spinner size="100" />
                            ) : (
                              <Icon size="100" src={Icons.Play} />
                            )
                          }
                          after={
                            <Icon
                              size="50"
                              src={Icons.Cross}
                              style={{ cursor: 'pointer' }}
                              onClick={(e: React.MouseEvent) => {
                                e.stopPropagation();
                                removeClip(clip.id);
                              }}
                            />
                          }
                        >
                          <Text size="B300" truncate style={{ maxWidth: '120px' }}>
                            {clip.name}
                          </Text>
                        </Chip>
                      </Box>
                    ))}
                  </Box>
                )}

                {error && (
                  <Text size="T200" style={{ color: color.Critical.Main }}>
                    {error}
                  </Text>
                )}
              </Box>
            </Menu>
          </FocusTrap>
        }
      >
        <TooltipProvider
          tooltip={
            <Tooltip>
              <Text>Soundboard</Text>
            </Tooltip>
          }
        >
          {(triggerRef) => (
            <IconButton
              ref={triggerRef}
              variant="Surface"
              fill="Soft"
              radii="400"
              size="400"
              onClick={handleOpen}
              outlined
              aria-label="Soundboard"
              aria-expanded={!!cords}
              aria-haspopup="menu"
            >
              <Icon size="400" src={Icons.BellRing} />
            </IconButton>
          )}
        </TooltipProvider>
      </PopOut>
    </>
  );
}
