import React from 'react'

import { nativePlayerProps } from '../../../utils/nativeMediaPlayers'
import Bubble from '../_shared/Bubble'
import DismissButton from '../_shared/DismissButton'
import {
  bubbleVariantForState,
  type AudioItem,
  type ComposerExtras,
  type MediaPreloadMode,
  type MessageAttachmentBaseProps,
  type MessageAttachmentState,
} from '../types'

export interface AudioAttachmentSharedProps extends MessageAttachmentBaseProps {
  /** Audio source URL (`mp3`, `aac`, `wav`, …). */
  src?: string
  /** MIME type hint — typed onto the inline `<source>` element. */
  mimeType?: string
  /**
   * Filename — used as the download default name (consumed by the
   * native player's kebab menu). The HTML `<audio>` element doesn't
   * expose a built-in title slot, so we don't render the filename
   * inside the bubble itself.
   */
  filename?: string
  /**
   * Stacked audio. Takes precedence over `src` when set. Each item
   * renders its own native `<audio controls>` player, vertically
   * stacked inside the same bubble with an 8px gap between players.
   * Sent + Received only — the composer surface accepts a single
   * attachment at a time.
   */
  items?: AudioItem[]
  /**
   * `<audio preload>` hint applied to every player on the bubble.
   * When omitted, the default depends on the rendered shape:
   *
   *   - Single audio (no `items`, or `items.length === 1`) →
   *     `'metadata'` so the native player surfaces duration
   *     immediately.
   *   - Stacked audio (`items.length > 1`) → `'none'` so a thread
   *     of voice memos doesn't fan out N parallel metadata requests
   *     on first paint.
   *
   * Per-track overrides live on `AudioItem.preload`.
   */
  preload?: MediaPreloadMode
}

const resolveItems = ({
  src,
  mimeType,
  filename,
  items,
}: {
  src?: string
  mimeType?: string
  filename?: string
  items?: AudioItem[]
}): AudioItem[] => {
  if (items && items.length > 0) return items
  if (src) return [{ src, mimeType, filename }]
  return []
}

const NativeAudioPlayer: React.FC<{
  item: AudioItem
  preload: MediaPreloadMode
  trailingAction?: React.ReactNode
}> = ({ item, preload, trailingAction }) => (
  <div className="flex items-center gap-2">
    {/* No `<track>` is rendered — we don't author caption sidecars
        for chat attachments, and an empty `<track kind="captions" />`
        emits a runtime warning. Re-add a real track (with `src`,
        `srcLang`, and `default`) once caption support actually ships.
        The `jsx-a11y/media-has-caption` rule is suppressed for the
        same reason. */}
    {/* eslint-disable-next-line jsx-a11y/media-has-caption */}
    <audio
      {...nativePlayerProps}
      src={item.src}
      controls
      preload={item.preload ?? preload}
      className="block min-w-0 flex-1"
    >
      {item.mimeType ? <source src={item.src} type={item.mimeType} /> : null}
    </audio>
    {trailingAction ?? null}
  </div>
)

interface InternalAudioRowProps extends AudioAttachmentSharedProps {
  state: MessageAttachmentState
  onDismiss?: () => void
}

const AudioAttachmentRow: React.FC<InternalAudioRowProps> = ({
  state,
  src,
  mimeType,
  filename,
  items,
  text,
  groupPosition,
  preload,
  onDismiss,
}) => {
  const variant = bubbleVariantForState(state)
  const showDismiss = state === 'composer' && !!onDismiss
  const resolvedItems = resolveItems({ src, mimeType, filename, items })

  if (resolvedItems.length === 0) {
    return null
  }

  // Default rule: surface duration immediately for a single player,
  // but avoid fanning out N metadata requests when the bubble is a
  // thread of voice memos. An explicit `preload` prop overrides;
  // per-track overrides live on `AudioItem.preload`.
  const resolvedPreload: MediaPreloadMode =
    preload ?? (resolvedItems.length > 1 ? 'none' : 'metadata')

  return (
    <Bubble
      variant={variant}
      text={text}
      groupPosition={groupPosition}
      data-testid="audio-attachment"
    >
      {/* Native `<audio controls>` already exposes a download in its
          kebab menu, so we don't render a separate Download button.
          The element also has no built-in title slot — the dismiss
          button (Composer only) sits inline next to the player.
          Stacked players get an 8px vertical gap so each track reads
          as a discrete attachment. */}
      <div className="flex flex-col gap-2">
        {resolvedItems.map((item, index) => (
          <NativeAudioPlayer
            key={`${item.src}-${index}`}
            item={item}
            preload={resolvedPreload}
            trailingAction={
              // Composer only supports a single attachment, so the
              // dismiss control sits on the only player.
              showDismiss && index === 0 ? (
                <DismissButton onClick={onDismiss!} variant="inline" />
              ) : undefined
            }
          />
        ))}
      </div>
    </Bubble>
  )
}

/**
 * Composer-only props. Single audio (`src`) only — the composer
 * surface accepts a single attachment at a time, so `items` is not
 * supported. Captions (`text`) and `groupPosition` are also dropped:
 * the composer renders a standalone draft, not part of a same-author
 * run, and captions live in the message-input textarea, not inside
 * the draft attachment preview.
 */
export interface AudioComposerProps extends ComposerExtras {
  src?: string
  mimeType?: string
  filename?: string
  /** See `AudioAttachmentSharedProps.preload`. */
  preload?: MediaPreloadMode
}
export type AudioSentProps = AudioAttachmentSharedProps
export type AudioReceivedProps = AudioAttachmentSharedProps

const AudioComposer: React.FC<AudioComposerProps> = (props) => (
  <AudioAttachmentRow {...props} state="composer" />
)
const AudioSent: React.FC<AudioSentProps> = (props) => (
  <AudioAttachmentRow {...props} state="sent" />
)
const AudioReceived: React.FC<AudioReceivedProps> = (props) => (
  <AudioAttachmentRow {...props} state="received" />
)

const AudioAttachment = {
  Composer: AudioComposer,
  Sent: AudioSent,
  Received: AudioReceived,
}

export default AudioAttachment
