import {
  useCallback,
  useId,
  useRef,
  type Dispatch,
  type SetStateAction,
} from 'react'
import { Loader2, Pause, Play, SkipBack, Square, Volume2 } from 'lucide-react'
import { useTtsPreferences } from '../context/TtsPreferencesContext'
import { extractTtsSentences } from '../lib/ttsSentenceSplit'
import {
  getTtsPitch,
  getTtsVoiceUri,
  TTS_SPEED_OPTIONS,
} from '../lib/ttsSettings'

/** Strip rough markdown / noise so speech sounds sane (aligned with phd-advisor MessageBubble idea). */
export function stripForSpeech(raw: string): string {
  if (!raw) return ''
  return raw
    .replace(/```[\s\S]*?```/g, ' ')
    .replace(/`([^`]+)`/g, '$1')
    .replace(/#{1,6}\s?/g, '')
    .replace(/[*_~]{1,3}([^*_~]+)[*_~]{1,3}/g, '$1')
    .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
    .replace(/!\[([^\]]*)\]\([^)]+\)/g, '$1')
    .replace(/^\s*[-*+]\s/gm, '')
    .replace(/\n{2,}/g, '. ')
    .replace(/\n/g, ' ')
    .replace(/\s+/g, ' ')
    .trim()
}

/** Shared across all message rows so starting playback in one row invalidates others (AskJerry-style). */
let ttsGen = 0
function bumpTtsGen(): number {
  ttsGen += 1
  return ttsGen
}

export type MessageTtsSharedState = {
  index: number | null
  phase: 'idle' | 'loading' | 'playing' | 'paused'
}

export function createInitialMessageTtsState(): MessageTtsSharedState {
  return { index: null, phase: 'idle' }
}

type Props = {
  text: string
  messageIndex: number
  tts: MessageTtsSharedState
  setTts: Dispatch<SetStateAction<MessageTtsSharedState>>
}

export function MessageTtsControls({
  text,
  messageIndex,
  tts,
  setTts,
}: Props) {
  const labelId = useId()
  const { playbackSpeed, setPlaybackSpeed } = useTtsPreferences()

  const speedRef = useRef(playbackSpeed)
  speedRef.current = playbackSpeed
  const skipRef = useRef(false)
  const replayDeltaRef = useRef<number | null>(null)

  const isMine =
    tts.index === messageIndex && tts.phase !== 'idle'
  const loading = isMine && tts.phase === 'loading'
  const playing = isMine && tts.phase === 'playing'
  const paused = isMine && tts.phase === 'paused'

  const speakSentence = useCallback((sentence: string, myGen: number): Promise<void> => {
    return new Promise((resolve) => {
      const synth = window.speechSynthesis
      if (!synth || myGen !== ttsGen) {
        resolve()
        return
      }
      const u = new SpeechSynthesisUtterance(sentence)
      u.rate = speedRef.current
      u.pitch = getTtsPitch()
      const vUri = getTtsVoiceUri()
      if (vUri) {
        const v = synth.getVoices().find((x) => x.voiceURI === vUri)
        if (v) u.voice = v
      }
      let settled = false
      const finish = () => {
        if (settled) return
        settled = true
        resolve()
      }
      const pollId = window.setInterval(() => {
        if (myGen !== ttsGen) {
          synth.cancel()
          finish()
        }
        if (skipRef.current) {
          synth.cancel()
          finish()
        }
      }, 120)
      u.onend = () => {
        window.clearInterval(pollId)
        finish()
      }
      u.onerror = () => {
        window.clearInterval(pollId)
        finish()
      }
      synth.speak(u)
    })
  }, [])

  const runPlayback = useCallback(
    async (myGen: number) => {
      const plain = stripForSpeech(text)
      const sentences = extractTtsSentences(plain, true)
      if (!sentences.length) {
        if (myGen === ttsGen) {
          setTts({ index: null, phase: 'idle' })
        }
        return
      }

      let playedCount = 0
      setTts({ index: messageIndex, phase: 'loading' })
      await Promise.resolve()
      if (myGen !== ttsGen) return
      setTts({ index: messageIndex, phase: 'playing' })

      while (playedCount < sentences.length) {
        if (myGen !== ttsGen) return
        await speakSentence(sentences[playedCount], myGen)
        if (myGen !== ttsGen) return
        if (skipRef.current) {
          skipRef.current = false
          const delta = replayDeltaRef.current ?? 0
          replayDeltaRef.current = null
          playedCount = Math.max(0, playedCount + delta)
          continue
        }
        playedCount++
      }
      if (myGen === ttsGen) {
        setTts({ index: null, phase: 'idle' })
      }
    },
    [messageIndex, setTts, speakSentence, text],
  )

  const onPrimary = () => {
    if (paused) {
      window.speechSynthesis?.resume()
      setTts({ index: messageIndex, phase: 'playing' })
      return
    }
    if (playing) {
      window.speechSynthesis?.pause()
      setTts({ index: messageIndex, phase: 'paused' })
      return
    }
    if (loading) return

    window.speechSynthesis?.cancel()
    const myGen = bumpTtsGen()
    skipRef.current = false
    replayDeltaRef.current = null
    void runPlayback(myGen)
  }

  const onReplay = () => {
    replayDeltaRef.current = -1
    skipRef.current = true
    window.speechSynthesis?.cancel()
  }

  const onStop = () => {
    bumpTtsGen()
    skipRef.current = false
    replayDeltaRef.current = null
    window.speechSynthesis?.cancel()
    setTts({ index: null, phase: 'idle' })
  }

  const cycleSpeed = () => {
    const i = TTS_SPEED_OPTIONS.indexOf(playbackSpeed)
    setPlaybackSpeed(TTS_SPEED_OPTIONS[(i + 1) % TTS_SPEED_OPTIONS.length])
  }

  const noText = !(text || '').trim()
  const primaryDisabled =
    loading || (noText && tts.index !== messageIndex)

  if (!window.speechSynthesis) {
    return null
  }

  return (
    <div className="message-tts-bar" role="group" aria-labelledby={labelId}>
      <span id={labelId} className="sr-only">
        Text-to-speech controls
      </span>
      <div className="message-tts-buttons">
        <button
          type="button"
          className={`message-tts-btn ${playing || paused ? 'message-tts-btn-active' : ''}`}
          onClick={onPrimary}
          disabled={primaryDisabled}
          title={
            paused
              ? 'Resume'
              : playing
                ? 'Pause'
                : loading
                  ? 'Loading speech…'
                  : 'Read aloud'
          }
          aria-label={
            paused
              ? 'Resume'
              : playing
                ? 'Pause'
                : loading
                  ? 'Loading speech'
                  : 'Read aloud'
          }
        >
          {paused ? (
            <Play size={14} aria-hidden />
          ) : loading ? (
            <Loader2 size={14} className="message-tts-loading-spin" aria-hidden />
          ) : playing ? (
            <Pause size={14} aria-hidden />
          ) : (
            <Volume2 size={14} aria-hidden />
          )}
        </button>
        {(playing || paused) && (
          <button
            type="button"
            className="message-tts-btn"
            onClick={onReplay}
            title="Skip back"
            aria-label="Skip back"
          >
            <SkipBack size={14} aria-hidden />
          </button>
        )}
        <button
          type="button"
          className="message-tts-btn message-tts-speed-btn"
          onClick={cycleSpeed}
          title="Playback speed"
          aria-label={`Playback speed: ${playbackSpeed}x`}
        >
          <span className="message-tts-speed-label">{playbackSpeed}x</span>
        </button>
        {(playing || paused || (loading && isMine)) && (
          <button
            type="button"
            className="message-tts-btn"
            onClick={onStop}
            title="Stop reading"
            aria-label="Stop reading"
          >
            <Square size={14} aria-hidden />
          </button>
        )}
      </div>
    </div>
  )
}
