import type { NodeKey } from 'lexical';
import type { JSX } from 'react';

import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection';
import { mergeRegister } from '@lexical/utils';
import {
  $setSelection,
  CLICK_COMMAND,
  COMMAND_PRIORITY_LOW,
  KEY_ESCAPE_COMMAND,
  SELECTION_CHANGE_COMMAND,
} from 'lexical';
import { useCallback, useEffect, useMemo, useRef } from 'react';

import { getS3Url } from '../s3Config';

interface AudioComponentProps {
  nodeKey: NodeKey;
  src: string;
  fileKey?: string;
  uploadProgress?: number;
  uploadError?: string;
}

export default function AudioComponent({
  nodeKey,
  src,
  fileKey,
  uploadProgress,
  uploadError,
}: AudioComponentProps): JSX.Element {
  const [editor] = useLexicalComposerContext();
  const [isSelected, setSelected, clearSelection] = useLexicalNodeSelection(nodeKey);
  const containerRef = useRef<HTMLDivElement | null>(null);
  const audioRef = useRef<HTMLAudioElement | null>(null);

  const resolvedSrc = useMemo(() => {
    if (fileKey) {
      return getS3Url(fileKey);
    }
    return src;
  }, [fileKey, src]);

  const isUploading = uploadProgress !== undefined && uploadProgress < 100 && !uploadError;

  const handleEscape = useCallback(() => {
    if (!isSelected) {
      return false;
    }
    $setSelection(null);
    editor.update(() => {
      setSelected(true);
      editor.getRootElement()?.focus();
    });
    return true;
  }, [editor, isSelected, setSelected]);

  const handleClick = useCallback(
    (event: MouseEvent) => {
      if (!containerRef.current) {
        return false;
      }
      if (containerRef.current.contains(event.target as Node)) {
        if (event.shiftKey) {
          setSelected(!isSelected);
        } else {
          clearSelection();
          setSelected(true);
        }
        return true;
      }
      return false;
    },
    [clearSelection, isSelected, setSelected],
  );

  useEffect(() => {
    return mergeRegister(
      editor.registerCommand(
        SELECTION_CHANGE_COMMAND,
        () => false,
        COMMAND_PRIORITY_LOW,
      ),
      editor.registerCommand(
        CLICK_COMMAND,
        handleClick,
        COMMAND_PRIORITY_LOW,
      ),
      editor.registerCommand(
        KEY_ESCAPE_COMMAND,
        handleEscape,
        COMMAND_PRIORITY_LOW,
      ),
    );
  }, [editor, handleClick, handleEscape]);

  const borderColor = uploadError
    ? 'rgba(244, 67, 54, 0.6)'
    : isSelected
    ? 'var(--accent-color, #4CAF50)'
    : 'var(--border-color, #ccc)';

  return (
    <div
      ref={containerRef}
      className="editor-audio-card"
      style={{
        borderColor,
      }}
      tabIndex={0}
      onFocus={() => {
        clearSelection();
        setSelected(true);
      }}
    >
      <div className="editor-audio-wrapper">
        {resolvedSrc ? (
          <audio
            ref={audioRef}
            className="editor-audio-player"
            controls
            preload="metadata"
            src={resolvedSrc}
          />
        ) : (
          <div className="editor-audio-placeholder">Preparing audio...</div>
        )}
        {isUploading && (
          <div className="editor-audio-overlay">
            <div className="editor-audio-progress">
              <div
                className="editor-audio-progress-value"
                style={{ width: `${uploadProgress || 0}%` }}
              />
            </div>
            <div className="editor-audio-progress-label">
              Uploading... {Math.round(uploadProgress || 0)}%
            </div>
          </div>
        )}
        {uploadError && (
          <div className="editor-audio-error">❌ {uploadError}</div>
        )}
      </div>
    </div>
  );
}
