import { useState, useRef, useCallback } from 'react'
import { Button } from '@/components/ui/button'
import { cn } from '@/lib/utils'
import { Mic, Loader2 } from 'lucide-react'
import { toast } from 'sonner'

type RecordingState = 'idle' | 'recording' | 'transcribing'

interface VoiceRecordButtonProps {
    /** Callback when transcription is complete */
    onTranscript: (text: string) => void
    /** Callback when an error occurs */
    onError?: (error: string) => void
    /** Whether the button is disabled */
    disabled?: boolean
    /** Additional CSS classes */
    className?: string
}

const MIN_RECORDING_MS = 500 // Minimum recording duration to ensure audio data

export function VoiceRecordButton({
    onTranscript,
    onError,
    disabled = false,
    className,
}: VoiceRecordButtonProps) {
    const [state, setState] = useState<RecordingState>('idle')
    const mediaRecorderRef = useRef<MediaRecorder | null>(null)
    const chunksRef = useRef<Blob[]>([])
    const streamRef = useRef<MediaStream | null>(null)
    const recordingStartTimeRef = useRef<number>(0)

    const cleanup = useCallback(() => {
        if (streamRef.current) {
            streamRef.current.getTracks().forEach(track => track.stop())
            streamRef.current = null
        }
        mediaRecorderRef.current = null
        chunksRef.current = []
    }, [])

    const sendForTranscription = useCallback(async (audioBlob: Blob) => {
        setState('transcribing')

        try {
            const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content')

            const formData = new FormData()
            formData.append('audio', audioBlob, 'recording.webm')

            const response = await fetch('/api/stt/transcribe', {
                method: 'POST',
                headers: {
                    'X-CSRF-TOKEN': csrfToken || '',
                    'X-Requested-With': 'XMLHttpRequest',
                },
                body: formData,
            })

            if (!response.ok) {
                throw new Error('Transcription failed')
            }

            const result = await response.json()

            if (result.error === 0 && result.data?.text) {
                onTranscript(result.data.text)
            } else {
                throw new Error(result.message || 'Transcription failed')
            }
        } catch (error) {
            const errorMessage = error instanceof Error ? error.message : 'Transcription failed'
            toast.error(errorMessage)
            onError?.(errorMessage)
        } finally {
            setState('idle')
            cleanup()
        }
    }, [onTranscript, onError, cleanup])

    const startRecording = useCallback(async () => {
        try {
            const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
            streamRef.current = stream

            const mediaRecorder = new MediaRecorder(stream, {
                mimeType: MediaRecorder.isTypeSupported('audio/webm') ? 'audio/webm' : 'audio/mp4',
            })
            mediaRecorderRef.current = mediaRecorder
            chunksRef.current = []

            mediaRecorder.ondataavailable = (e) => {
                if (e.data.size > 0) {
                    chunksRef.current.push(e.data)
                }
            }

            mediaRecorder.onstop = () => {
                const audioBlob = new Blob(chunksRef.current, { type: mediaRecorder.mimeType })
                if (audioBlob.size > 1000) {
                    sendForTranscription(audioBlob)
                } else {
                    toast.error('Recording too short, please hold longer')
                    setState('idle')
                    cleanup()
                }
            }

            // Use timeslice to collect data every 100ms for reliable recording
            mediaRecorder.start(100)
            recordingStartTimeRef.current = Date.now()
            setState('recording')
        } catch (error) {
            const errorMessage = error instanceof Error ? error.message : 'Microphone access denied'
            toast.error(errorMessage)
            onError?.(errorMessage)
            cleanup()
        }
    }, [sendForTranscription, onError, cleanup])

    const stopRecording = useCallback(() => {
        if (mediaRecorderRef.current && state === 'recording') {
            const elapsed = Date.now() - recordingStartTimeRef.current
            const remaining = MIN_RECORDING_MS - elapsed

            if (remaining > 0) {
                // Wait for minimum recording time before stopping
                setTimeout(() => {
                    if (mediaRecorderRef.current?.state === 'recording') {
                        mediaRecorderRef.current.stop()
                    }
                }, remaining)
            } else {
                mediaRecorderRef.current.stop()
            }
        }
    }, [state])

    const handlePointerDown = useCallback((e: React.PointerEvent) => {
        e.preventDefault()
        if (state === 'idle' && !disabled) {
            startRecording()
        }
    }, [state, disabled, startRecording])

    const handlePointerUp = useCallback((e: React.PointerEvent) => {
        e.preventDefault()
        if (state === 'recording') {
            stopRecording()
        }
    }, [state, stopRecording])

    const handlePointerLeave = useCallback((e: React.PointerEvent) => {
        e.preventDefault()
        if (state === 'recording') {
            stopRecording()
        }
    }, [state, stopRecording])

    // Prevent context menu on long press (mobile)
    const handleContextMenu = useCallback((e: React.MouseEvent) => {
        e.preventDefault()
    }, [])

    const getIcon = () => {
        switch (state) {
            case 'transcribing':
                return <Loader2 className="h-4 w-4 animate-spin" />
            case 'recording':
                return <Mic className="h-4 w-4" />
            default:
                return <Mic className="h-4 w-4" />
        }
    }

    const getTitle = () => {
        switch (state) {
            case 'transcribing':
                return 'Transcribing...'
            case 'recording':
                return 'Release to stop'
            default:
                return 'Hold to record'
        }
    }

    return (
        <Button
            type="button"
            variant={state === 'recording' ? 'destructive' : 'outline'}
            size="icon"
            disabled={disabled || state === 'transcribing'}
            onPointerDown={handlePointerDown}
            onPointerUp={handlePointerUp}
            onPointerLeave={handlePointerLeave}
            onContextMenu={handleContextMenu}
            className={cn(
                'touch-none select-none',
                state === 'recording' && 'animate-pulse',
                className
            )}
            title={getTitle()}
        >
            {getIcon()}
        </Button>
    )
}
