"use client";

import { useEffect, useId, useRef, useState, type FormEvent, type RefObject } from "react";
import { Mic, SendHorizontal } from "lucide-react";
import { LANGUAGE_META, type ChatCopy, type LanguageCode } from "@/lib/chat/i18n";
import { MESSAGE_MAX_LENGTH } from "@/lib/chat/useChat";
import { cn } from "@/lib/utils";

type SpeechRecognitionLike = {
  lang: string;
  interimResults: boolean;
  maxAlternatives: number;
  onresult: ((event: { results: ArrayLike<ArrayLike<{ transcript: string }>> }) => void) | null;
  onerror: (() => void) | null;
  onstart: (() => void) | null;
  onend: (() => void) | null;
  start: () => void;
  stop: () => void;
};

type SpeechWindow = Window & {
  SpeechRecognition?: new () => SpeechRecognitionLike;
  webkitSpeechRecognition?: new () => SpeechRecognitionLike;
};

type Props = {
  language: LanguageCode;
  copy: ChatCopy;
  /** Offline, a turn in flight, or a 429 cooldown. */
  disabled: boolean;
  /** No connection: the whole composer is disabled, not just Send. */
  offline: boolean;
  cooldownLeft: number;
  onSend: (text: string) => void;
  inputRef: RefObject<HTMLInputElement | null>;
};

export default function Composer({ language, copy, disabled, offline, cooldownLeft, onSend, inputRef }: Props) {
  const inputId = useId();
  const [input, setInput] = useState("");
  const [isListening, setIsListening] = useState(false);
  const [voiceError, setVoiceError] = useState("");
  const recognitionRef = useRef<SpeechRecognitionLike | null>(null);
  // Read inside the recognition callbacks, which outlive the render they were created in.
  const disabledRef = useRef(disabled);
  const onSendRef = useRef(onSend);

  useEffect(() => {
    disabledRef.current = disabled;
    onSendRef.current = onSend;
  }, [disabled, onSend]);

  useEffect(() => {
    return () => {
      recognitionRef.current?.stop();
    };
  }, []);

  // A recogniser started in one language must not keep transcribing after a switch.
  useEffect(() => {
    recognitionRef.current?.stop();
  }, [language]);

  const submit = (text: string) => {
    const trimmed = text.trim();
    if (!trimmed || disabled) return;
    onSend(trimmed);
    setInput("");
    setVoiceError("");
  };

  const onSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    submit(input);
  };

  const startVoiceInput = () => {
    if (isListening) {
      recognitionRef.current?.stop();
      return;
    }

    const speechWindow = window as SpeechWindow;
    const Recognition = speechWindow.SpeechRecognition || speechWindow.webkitSpeechRecognition;
    if (!Recognition) {
      setVoiceError(copy.voiceUnsupported);
      return;
    }

    setVoiceError("");
    const recognition = new Recognition();
    recognition.lang = LANGUAGE_META[language].locale;
    recognition.interimResults = false;
    recognition.maxAlternatives = 1;

    recognition.onstart = () => setIsListening(true);
    recognition.onend = () => setIsListening(false);
    recognition.onerror = () => {
      setIsListening(false);
      setVoiceError(copy.voiceError);
    };
    recognition.onresult = (event) => {
      const transcript = event.results?.[0]?.[0]?.transcript?.trim() || "";
      if (!transcript) return;
      if (disabledRef.current) {
        // Can't send right now — leave it in the box to send later.
        setInput(transcript.slice(0, MESSAGE_MAX_LENGTH));
        return;
      }
      onSendRef.current(transcript);
      setInput("");
    };

    recognitionRef.current = recognition;
    try {
      recognition.start();
    } catch {
      setIsListening(false);
      setVoiceError(copy.voiceError);
    }
  };

  const status = voiceError || (isListening ? copy.listeningText : "");
  const cooling = !status && cooldownLeft > 0;

  return (
    <div className="shrink-0 border-t border-red-100 bg-white px-3 pb-[calc(0.75rem+env(safe-area-inset-bottom))] pt-2.5 sm:pb-3">
      <form onSubmit={onSubmit} className="flex items-center gap-2">
        <label htmlFor={inputId} className="sr-only">
          {copy.inputPlaceholder}
        </label>
        <input
          id={inputId}
          ref={inputRef}
          type="text"
          value={input}
          maxLength={MESSAGE_MAX_LENGTH}
          onChange={(event) => setInput(event.target.value)}
          placeholder={copy.inputPlaceholder}
          enterKeyHint="send"
          autoComplete="off"
          disabled={offline}
          className="h-11 min-w-0 flex-1 rounded-xl border border-gray-200 px-3 text-sm text-gray-800 placeholder:text-gray-400 focus:border-[#CC1414] focus:outline-none focus:ring-2 focus:ring-[#CC1414]/20 disabled:bg-gray-50"
        />
        <button
          type="button"
          onClick={startVoiceInput}
          disabled={offline}
          aria-pressed={isListening}
          className={cn(
            "inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-xl border transition-colors",
            isListening
              ? "border-emerald-300 bg-emerald-50 text-emerald-700"
              : "border-gray-200 bg-white text-[#CC1414] hover:bg-red-50",
            "disabled:cursor-not-allowed disabled:opacity-50"
          )}
          aria-label={copy.voiceInput}
        >
          <Mic size={16} className={isListening ? "animate-pulse" : ""} aria-hidden />
        </button>
        <button
          type="submit"
          disabled={disabled || !input.trim()}
          className="inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-[#CC1414] text-white transition-colors hover:bg-[#A50E0E] disabled:cursor-not-allowed disabled:opacity-50"
          aria-label={copy.sendMessage}
        >
          <SendHorizontal size={16} aria-hidden />
        </button>
      </form>
      {status ? (
        <p role="status" className={cn("mt-1.5 text-[11px]", voiceError ? "text-red-500" : "text-gray-500")}>
          {status}
        </p>
      ) : null}
      {/* Not a live region: it changes every second. The 429 bubble already announced the wait. */}
      {cooling ? <p className="mt-1.5 text-[11px] text-gray-500">{copy.cooldown(cooldownLeft)}</p> : null}
    </div>
  );
}
