import {
  useRef,
  useState,
  useEffect,
  useCallback,
} from 'react';
import {
  View,
  Text,
  TextInput,
  FlatList,
  TouchableOpacity,
  Platform,
  Animated,
  Keyboard,
  KeyboardAvoidingView,
  StyleSheet,
  ActivityIndicator,
  NativeSyntheticEvent,
  NativeScrollEvent,
} from 'react-native';
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
import { useRouter } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';
import { Audio } from 'expo-av';
// TODO(sdk54): migrate to new expo-file-system class-based API (File/Directory/Paths) — see Task #14
import * as FileSystem from 'expo-file-system/legacy';
import Constants from 'expo-constants';
import { useTranslation } from 'react-i18next';
import { RiveAvatar, type Emotion } from '../components/RiveAvatar';
import { useCoachStore, type Message } from '../stores/coach';
import { apiFetch } from '../lib/api';
import { supabase } from '../lib/supabase';
import { useColors } from '../lib/theme';
import { useThemeStore } from '../stores/theme';
import { detectEmotion } from '../lib/lyraResponse';

function formatDuration(s: number): string {
  const m = Math.floor(s / 60);
  const sec = (s % 60).toString().padStart(2, '0');
  return `${m}:${sec}`;
}

function formatTimestamp(date: Date): string {
  return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
}

// ── Loading skeleton ──────────────────────────────────────────────────────────
// Standard-Spinner — kein zweiter Rive-Avatar (der ist bereits im topBar oben).

function LoadingPulse() {
  const colors = useColors();
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', gap: 12 }}>
      <ActivityIndicator size="large" color={colors.textMuted} />
    </View>
  );
}

// ── Thinking dots ─────────────────────────────────────────────────────────────

function ThinkingDots() {
  const colors = useColors();
  const anim = useRef([new Animated.Value(0), new Animated.Value(0), new Animated.Value(0)]).current;

  useEffect(() => {
    const animations = anim.map((a, i) =>
      Animated.loop(
        Animated.sequence([
          Animated.delay(i * 160),
          Animated.timing(a, { toValue: 1, duration: 300, useNativeDriver: true }),
          Animated.timing(a, { toValue: 0, duration: 300, useNativeDriver: true }),
        ])
      )
    );
    animations.forEach((a) => a.start());
    return () => animations.forEach((a) => a.stop());
  }, []);

  return (
    <View style={styles.thinkingRow}>
      {anim.map((a, i) => (
        <Animated.View
          key={i}
          style={[
            styles.thinkingDot,
            { backgroundColor: colors.border, transform: [{ translateY: a.interpolate({ inputRange: [0, 1], outputRange: [0, -5] }) }] },
          ]}
        />
      ))}
    </View>
  );
}

// ── Voice bars ────────────────────────────────────────────────────────────────

function VoiceBars({ count, baseColor }: { count: number; baseColor: string }) {
  const anims = useRef(Array.from({ length: count }, () => new Animated.Value(4))).current;

  useEffect(() => {
    const animations = anims.map((a, i) =>
      Animated.loop(
        Animated.sequence([
          Animated.timing(a, { toValue: 4 + Math.random() * 14, duration: 450 + (i % 5) * 80, useNativeDriver: false }),
          Animated.timing(a, { toValue: 4, duration: 450 + (i % 5) * 80, useNativeDriver: false }),
        ])
      )
    );
    animations.forEach((a) => a.start());
    return () => animations.forEach((a) => a.stop());
  }, []);

  return (
    <View style={{ flexDirection: 'row', alignItems: 'center', gap: 2, height: 20 }}>
      {anims.map((a, i) => (
        <Animated.View
          key={i}
          style={{ width: 2.5, height: a, borderRadius: 2, backgroundColor: baseColor, opacity: 0.75 }}
        />
      ))}
    </View>
  );
}

// ── Message row (Insta-DM style) ──────────────────────────────────────────────

type MessageWithMeta = Message & { timestamp: Date };

function MessageRow({
  item,
  t,
}: {
  item: MessageWithMeta;
  t: (key: string) => string;
}) {
  const colors = useColors();
  const isUser = item.role === 'user';

  return (
    <View style={[styles.msgRow, isUser ? styles.msgRowUser : styles.msgRowAssistant]}>
      <View style={[styles.bubbleCol, isUser ? styles.bubbleColUser : styles.bubbleColAssistant]}>
        <View style={[styles.bubble, isUser ? styles.bubbleUser : [styles.bubbleAssistant, { backgroundColor: colors.surfaceElevated }]]}>
          <Text style={[styles.bubbleText, isUser ? styles.bubbleTextUser : [styles.bubbleTextAssistant, { color: colors.text }]]}>
            {item.content}
          </Text>
        </View>

        <View style={[styles.metaRow, isUser ? styles.metaRowUser : styles.metaRowAssistant]}>
          {item.feedbackSaved && (
            <>
              <Ionicons name="checkmark-circle" size={11} color={colors.success} />
              <Text style={[styles.feedbackText, { color: colors.success }]}>{t('coach.feedback_saved')}</Text>
            </>
          )}
          <Text style={[styles.timestampText, { color: colors.textMuted }]}>{formatTimestamp(item.timestamp)}</Text>
        </View>
      </View>
    </View>
  );
}

// ── Main screen ───────────────────────────────────────────────────────────────

export default function CoachScreen() {
  const { t, i18n } = useTranslation();
  const router = useRouter();
  const insets = useSafeAreaInsets();
  const flatRef = useRef<FlatList>(null);
  const colors = useColors();
  const colorScheme = useThemeStore((s) => s.colorScheme);

  // Reaktive Slices — nur was UI-relevant ist (Re-Render bei diesen).
  const messages = useCoachStore((s) => s.messages);
  const thinking = useCoachStore((s) => s.thinking);
  // Actions sind stable — keine Re-Renders bei Bezug.
  const loadHistory = useCoachStore((s) => s.loadHistory);
  const clearHistory = useCoachStore((s) => s.clearHistory);
  const sendMessage = useCoachStore((s) => s.sendMessage);
  const pushMessage = useCoachStore((s) => s.pushMessage);
  const markFeedbackSaved = useCoachStore((s) => s.markFeedbackSaved);
  const setThinking = useCoachStore((s) => s.setThinking);
  const setWelcomeBackShown = useCoachStore((s) => s.setWelcomeBackShown);

  const [input, setInput] = useState('');
  const [emotion, setEmotion] = useState<Emotion>('idle');
  const [isSpeaking, setIsSpeaking] = useState(false);
  const [isRecording, setIsRecording] = useState(false);
  const [isTranscribing, setIsTranscribing] = useState(false);
  const [recordingDuration, setRecordingDuration] = useState(0);
  const [showScrollBtn, setShowScrollBtn] = useState(false);
  const [isLoading, setIsLoading] = useState(false);
  const [keyboardHeight, setKeyboardHeight] = useState(0);

  const recordingRef = useRef<Audio.Recording | null>(null);
  const soundRef = useRef<Audio.Sound | null>(null);
  const micHeld = useRef(false);
  const recordingTimer = useRef<ReturnType<typeof setInterval> | null>(null);
  const typingTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
  const emotionTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
  const isNearBottomRef = useRef(true);

  // Load history + welcome-back. Beide Side-Effects sind store-cached:
  //  - historyLoaded   → kein Re-Fetch + kein Spinner-Blink bei Tab-Wechsel
  //  - welcomeBackShownThisSession → keine doppelte Lyra-Begrüßung
  useEffect(() => {
    let cancelled = false;

    // Aktuelle Werte aus dem Store lesen (statt Closure-Stale beim ersten Render).
    const snap = useCoachStore.getState();
    const needsHistory = !snap.historyLoaded;
    const needsWelcomeBack = !snap.welcomeBackShownThisSession;

    if (!needsHistory && !needsWelcomeBack) {
      // Coach war diese Session schon offen → instant rendern, kein Spinner.
      requestAnimationFrame(() => flatRef.current?.scrollToEnd({ animated: false }));
      return;
    }

    async function init() {
      // Spinner nur wenn wir wirklich History fetchen müssen (erster Coach-Open).
      if (needsHistory) setIsLoading(true);

      if (needsHistory) {
        try {
          await loadHistory();
        } catch {
          // non-fatal
        }
      }

      if (cancelled) return;

      if (needsWelcomeBack) {
        try {
          const res = await apiFetch<{ message?: string }>('/api/lyra/welcome-back');
          if (!cancelled && res?.message) {
            pushMessage({ id: 'wb-' + Date.now(), role: 'assistant', content: res.message });
          }
        } catch {
          // no welcome-back — silent
        } finally {
          if (!cancelled) setWelcomeBackShown(true);
        }
      }

      if (!cancelled) {
        if (needsHistory) setIsLoading(false);
        requestAnimationFrame(() => flatRef.current?.scrollToEnd({ animated: false }));
      }
    }

    init();
    return () => {
      cancelled = true;
    };
  }, []);

  useEffect(() => {
    // iOS: Will*-Events feuern VOR der Keyboard-Animation → paddingBottom
    // ändert sich synchron mit KeyboardAvoidingView. Sonst springt der
    // Input erst hoch, dann nach unten ("Zucken").
    // Android: Will*-Events feuern unzuverlässig → Did* ist der stabile Pfad.
    const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
    const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
    const show = Keyboard.addListener(showEvent, (e) => setKeyboardHeight(e.endCoordinates.height));
    const hide = Keyboard.addListener(hideEvent, () => setKeyboardHeight(0));
    return () => { show.remove(); hide.remove(); };
  }, []);

  // Enrich messages with timestamp
  const enrichedMessages: MessageWithMeta[] = messages.map((msg) => ({
    ...msg,
    timestamp: new Date(),
  }));

  // Scroll to bottom when messages change (only when near bottom)
  useEffect(() => {
    if (messages.length === 0) return;
    if (isNearBottomRef.current) {
      requestAnimationFrame(() => flatRef.current?.scrollToEnd({ animated: true }));
    } else {
      setShowScrollBtn(true);
    }
  }, [messages.length, thinking]);

  function handleScroll(e: NativeSyntheticEvent<NativeScrollEvent>) {
    const { layoutMeasurement, contentOffset, contentSize } = e.nativeEvent;
    const distFromBottom = contentSize.height - contentOffset.y - layoutMeasurement.height;
    isNearBottomRef.current = distFromBottom < 80;
    if (isNearBottomRef.current) setShowScrollBtn(false);
  }

  function scrollToBottom() {
    flatRef.current?.scrollToEnd({ animated: true });
    setShowScrollBtn(false);
  }

  function handleInputChange(text: string) {
    setInput(text);
    if (text.length > 0) {
      setEmotion((e) => (e === 'thinking' ? e : 'happy'));
      if (typingTimer.current) clearTimeout(typingTimer.current);
      typingTimer.current = setTimeout(() => {
        setEmotion((e) => (e === 'happy' ? 'idle' : e));
      }, 2500);
    } else {
      if (typingTimer.current) clearTimeout(typingTimer.current);
      setEmotion((e) => (e === 'happy' ? 'idle' : e));
    }
  }

  function scheduleEmotionReset(delay = 4000) {
    if (emotionTimer.current) clearTimeout(emotionTimer.current);
    emotionTimer.current = setTimeout(() => setEmotion('idle'), delay);
  }

  async function handleSend() {
    const content = input.trim();
    if (!content || thinking) return;

    const userMsgId = Date.now().toString();
    pushMessage({ id: userMsgId, role: 'user', content });
    setInput('');
    setThinking(true);
    setEmotion('thinking');

    try {
      const res = await sendMessage(content, i18n.language);
      if (res.feedbackSaved) markFeedbackSaved(userMsgId);
      pushMessage({ id: (Date.now() + 1).toString(), role: 'assistant', content: res.message });
      const e = detectEmotion(res.message);
      setEmotion(e);
      scheduleEmotionReset(e === 'empathy' ? 6000 : 4000);
    } catch {
      pushMessage({ id: (Date.now() + 1).toString(), role: 'assistant', content: t('coach.error'), isError: true });
      setEmotion('idle');
    } finally {
      setThinking(false);
    }
  }

  async function handleVoiceSend(text: string) {
    if (!text.trim() || thinking) return;
    const userMsgId = Date.now().toString();
    pushMessage({ id: userMsgId, role: 'user', content: text });
    setThinking(true);
    setEmotion('thinking');

    try {
      const res = await sendMessage(text, i18n.language);
      if (res.feedbackSaved) markFeedbackSaved(userMsgId);
      pushMessage({ id: (Date.now() + 1).toString(), role: 'assistant', content: res.message });
      const e = detectEmotion(res.message);
      setEmotion(e);

      try {
        const apiBase = Constants.expoConfig?.extra?.apiUrl as string;
        const session = (await supabase.auth.getSession()).data.session;
        const speakUrl = `${apiBase}/api/coach/speak`;
        console.log('[tts] POST', speakUrl, 'text-len:', res.message.length);
        const ttsRes = await fetch(speakUrl, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            ...(session?.access_token ? { Authorization: `Bearer ${session.access_token}` } : {}),
          },
          body: JSON.stringify({ text: res.message, mode: 'chat' }),
        });
        console.log('[tts] response status:', ttsRes.status, 'content-type:', ttsRes.headers.get('content-type'));
        if (ttsRes.ok) {
          // Backend streamt audio/mpeg direkt — als ArrayBuffer holen, base64-encoden, in tmp-File schreiben.
          const buffer = await ttsRes.arrayBuffer();
          if (buffer.byteLength === 0) {
            console.warn('[tts] empty audio buffer');
          } else {
            const bytes = new Uint8Array(buffer);
            // Chunked base64-Encoding (RN btoa kann mit großen Strings überlaufen)
            let binary = '';
            const chunkSize = 0x8000;
            for (let i = 0; i < bytes.length; i += chunkSize) {
              binary += String.fromCharCode(
                ...bytes.subarray(i, Math.min(i + chunkSize, bytes.length))
              );
            }
            // eslint-disable-next-line no-undef
            const base64 = global.btoa ? global.btoa(binary) : Buffer.from(binary, 'binary').toString('base64');
            const tmpPath = `${FileSystem.cacheDirectory}lyra-tts-${Date.now()}.mp3`;
            await FileSystem.writeAsStringAsync(tmpPath, base64, {
              encoding: FileSystem.EncodingType.Base64,
            });

            const { sound } = await Audio.Sound.createAsync({ uri: tmpPath });
            soundRef.current = sound;
            setIsSpeaking(true);
            sound.setOnPlaybackStatusUpdate((status) => {
              if (status.isLoaded && status.didJustFinish) {
                setIsSpeaking(false);
                scheduleEmotionReset(0);
                sound.unloadAsync();
              }
            });
            await sound.playAsync();
          }
        } else {
          console.warn('[tts] backend error:', ttsRes.status, await ttsRes.text());
        }
      } catch (err) {
        console.warn('[tts] exception:', err);
        scheduleEmotionReset(e === 'empathy' ? 6000 : 4000);
      }
    } catch {
      pushMessage({ id: (Date.now() + 1).toString(), role: 'assistant', content: t('coach.error'), isError: true });
      setEmotion('idle');
    } finally {
      setThinking(false);
    }
  }

  function stopSpeaking() {
    soundRef.current?.stopAsync();
    soundRef.current?.unloadAsync();
    soundRef.current = null;
    setIsSpeaking(false);
    setEmotion('idle');
  }

  function startRecordingTimer() {
    setRecordingDuration(0);
    recordingTimer.current = setInterval(() => setRecordingDuration((d) => d + 1), 1000);
  }
  function stopRecordingTimer() {
    if (recordingTimer.current) clearInterval(recordingTimer.current);
    recordingTimer.current = null;
    setRecordingDuration(0);
  }

  async function onMicDown() {
    if (thinking || isTranscribing || isRecording || micHeld.current) return;
    if (isSpeaking) stopSpeaking();

    const { status } = await Audio.requestPermissionsAsync();
    if (status !== 'granted') return;

    micHeld.current = true;
    await Audio.setAudioModeAsync({ allowsRecordingIOS: true, playsInSilentModeIOS: true });
    const rec = new Audio.Recording();
    try {
      await rec.prepareToRecordAsync(Audio.RecordingOptionsPresets.HIGH_QUALITY);
      await rec.startAsync();
      recordingRef.current = rec;
      setIsRecording(true);
      startRecordingTimer();
    } catch {
      micHeld.current = false;
      await Audio.setAudioModeAsync({ allowsRecordingIOS: false });
    }
  }

  async function cancelRecording() {
    if (!isRecording) return;
    micHeld.current = false;
    stopRecordingTimer();
    setIsRecording(false);
    try {
      await recordingRef.current?.stopAndUnloadAsync();
    } catch { /* already stopped */ }
    recordingRef.current = null;
    await Audio.setAudioModeAsync({ allowsRecordingIOS: false });
  }

  async function onMicUp() {
    if (!micHeld.current || !isRecording) return;
    micHeld.current = false;
    stopRecordingTimer();
    setIsRecording(false);

    const rec = recordingRef.current;
    recordingRef.current = null;
    if (!rec) return;

    try {
      await rec.stopAndUnloadAsync();
    } catch { /* already stopped */ }
    await Audio.setAudioModeAsync({ allowsRecordingIOS: false });

    const uri = rec.getURI();
    console.log('[voice] URI after stop:', uri);
    if (!uri) return;

    setIsTranscribing(true);
    setEmotion('happy');

    try {
      // Backend erwartet base64-Audio in JSON-Body (NICHT FormData):
      //   { audio: base64String, mimeType: 'audio/m4a', language: 'de' }
      const base64 = await FileSystem.readAsStringAsync(uri, {
        encoding: FileSystem.EncodingType.Base64,
      });

      const session = (await supabase.auth.getSession()).data.session;
      const apiUrl = Constants.expoConfig?.extra?.apiUrl as string;
      console.log('[voice] POST', `${apiUrl}/api/coach/transcribe`, 'language:', i18n.language, 'base64-bytes:', base64.length);

      const res = await fetch(`${apiUrl}/api/coach/transcribe`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          ...(session?.access_token ? { Authorization: `Bearer ${session.access_token}` } : {}),
        },
        body: JSON.stringify({
          audio: base64,
          mimeType: 'audio/m4a',
          language: i18n.language,
        }),
      });

      const json = await res.json();
      console.log('[voice] transcribe response:', JSON.stringify(json).slice(0, 200));
      const text: string = json?.data?.text ?? json?.text ?? '';
      console.log('[voice] extracted text:', JSON.stringify(text));
      if (text.trim()) {
        await handleVoiceSend(text);
      }
    } catch (err) {
      console.warn('[voice] transcribe error:', err);
    } finally {
      setIsTranscribing(false);
      setEmotion((e) => (e === 'happy' ? 'idle' : e));
    }
  }

  const handleNewChat = useCallback(async () => {
    await clearHistory();
    setEmotion('idle');
  }, [clearHistory]);

  const renderMessage = useCallback(
    ({ item }: { item: MessageWithMeta }) => <MessageRow item={item} t={t} />,
    [t]
  );

  return (
    <SafeAreaView style={[styles.container, { backgroundColor: colors.bg }]} edges={['top']}>
      {/* Backdrop hinter topBar — verhindert dass Chat-Texte hinter dem Avatar */}
      {/* sichtbar werden ("kollidieren mit Lyra schriftzug"). */}
      <View
        style={[styles.topBarBackdrop, { height: insets.top + 170, backgroundColor: colors.bg }]}
        pointerEvents="none"
      />

      {/* Floating header — no bar, avatar + 2 icon buttons hover over chat */}
      <View style={[styles.topBar, { top: insets.top + 6 }]}>
        <TouchableOpacity style={[styles.backBtn, { backgroundColor: colorScheme === 'dark' ? 'rgba(44,44,46,0.92)' : 'rgba(255,255,255,0.92)' }]} onPress={() => router.replace('/(app)' as never)} hitSlop={12} activeOpacity={0.7}>
          <Ionicons name="chevron-back" size={24} color={colors.text} />
        </TouchableOpacity>

        <View style={styles.avatarCenter}>
          <View pointerEvents="none">
            <RiveAvatar emotion={emotion} size="md" />
          </View>
          <View style={styles.avatarMeta}>
            <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
              <Text style={[styles.avatarName, { color: colors.text }]}>{t('coach.lyra')}</Text>
              <View
                style={{
                  paddingHorizontal: 6,
                  paddingVertical: 2,
                  borderRadius: 6,
                  backgroundColor: colors.brandOrange + '22',
                  borderWidth: 1,
                  borderColor: colors.brandOrange + '55',
                }}
              >
                <Text
                  style={{
                    fontSize: 10,
                    fontFamily: 'Nunito_700Bold',
                    color: colors.brandOrange,
                    letterSpacing: 0.5,
                  }}
                >
                  {t('coach.modeBadge.coach')}
                </Text>
              </View>
            </View>
            {isSpeaking && (
              <View style={styles.speakingRow}>
                <VoiceBars count={5} baseColor={colors.brandOrange} />
                <Text style={[styles.speakingLabel, { color: colors.brandOrange }]}>{t('coach.speaking')}</Text>
                <TouchableOpacity style={[styles.stopBtn, { backgroundColor: colors.surfaceElevated }]} onPress={stopSpeaking} hitSlop={6} activeOpacity={0.7}>
                  <Ionicons name="square" size={10} color={colors.brandOrange} />
                </TouchableOpacity>
              </View>
            )}
          </View>
        </View>

        <TouchableOpacity style={[styles.newChatBtn, { backgroundColor: colorScheme === 'dark' ? 'rgba(44,44,46,0.92)' : 'rgba(255,255,255,0.92)' }]} onPress={handleNewChat} hitSlop={12} activeOpacity={0.7}>
          <Ionicons name="refresh-outline" size={22} color={colors.textMuted} />
        </TouchableOpacity>
      </View>

      {/* Content area */}
      <KeyboardAvoidingView
        style={{ flex: 1 }}
        behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
        keyboardVerticalOffset={0}
      >
        {isLoading ? (
          <LoadingPulse />
        ) : (
          <View style={{ flex: 1 }}>
            <FlatList
              ref={flatRef}
              data={enrichedMessages}
              renderItem={renderMessage}
              keyExtractor={(m) => m.id}
              contentContainerStyle={[styles.listContent, { paddingTop: insets.top + 180 }]}
              showsVerticalScrollIndicator={false}
              onScroll={handleScroll}
              scrollEventThrottle={100}
              onContentSizeChange={() => {
                if (isNearBottomRef.current) {
                  flatRef.current?.scrollToEnd({ animated: false });
                }
              }}
              ListFooterComponent={
                thinking ? (
                  <View style={styles.msgRowAssistant}>
                    <View style={[styles.bubbleAssistant, { backgroundColor: colors.surfaceElevated }]}>
                      <ThinkingDots />
                    </View>
                  </View>
                ) : null
              }
            />

            {/* Scroll-to-bottom button */}
            {showScrollBtn && (
              <TouchableOpacity style={styles.scrollDownBtn} onPress={scrollToBottom} activeOpacity={0.8}>
                <Ionicons name="chevron-down" size={18} color="#fff" />
              </TouchableOpacity>
            )}
          </View>
        )}

        {/* Input bar */}
        <View style={[styles.inputBar, { paddingBottom: keyboardHeight > 0 ? 8 : Math.max(12, insets.bottom), backgroundColor: colors.bg, borderTopColor: colors.border }]}>
          {isRecording ? (
            <View style={styles.recordingContainer}>
              <TouchableOpacity style={styles.cancelBtn} onPress={cancelRecording} activeOpacity={0.7}>
                <Ionicons name="trash" size={16} color="#f87171" />
              </TouchableOpacity>
              <View style={styles.pulseDot} />
              <Text style={styles.recordingTimer}>{formatDuration(recordingDuration)}</Text>
              <View style={{ flex: 1 }}>
                <VoiceBars count={18} baseColor="#f87171" />
              </View>
            </View>
          ) : isTranscribing ? (
            <View style={styles.transcribingRow}>
              <Ionicons name="sync" size={16} color={colors.textMuted} />
              <Text style={[styles.transcribingText, { color: colors.textMuted }]}>{t('coach.transcribing')}</Text>
            </View>
          ) : (
            <TextInput
              style={[styles.textInput, { backgroundColor: colors.surfaceElevated, color: colors.text }]}
              placeholder={t('coach.placeholder')}
              placeholderTextColor={colors.textMuted}
              value={input}
              onChangeText={handleInputChange}
              multiline
              maxLength={1000}
              returnKeyType="send"
              onSubmitEditing={handleSend}
            />
          )}

          {!isTranscribing && (
            <TouchableOpacity
              style={[styles.micBtn, { backgroundColor: colors.surfaceElevated }, isRecording && styles.micBtnActive, thinking && styles.micBtnDisabled]}
              onPressIn={onMicDown}
              onPressOut={onMicUp}
              disabled={thinking}
              activeOpacity={0.7}
            >
              <Ionicons
                name={isRecording ? 'square' : 'mic'}
                size={18}
                color={isRecording ? '#fff' : colors.textMuted}
              />
            </TouchableOpacity>
          )}

          {!isRecording && !isTranscribing && input.trim() !== '' && (
            <TouchableOpacity
              style={[styles.sendBtn, thinking && styles.sendBtnDisabled]}
              onPress={handleSend}
              disabled={thinking || !input.trim()}
              activeOpacity={0.7}
            >
              <Ionicons name="send" size={16} color="#fff" />
            </TouchableOpacity>
          )}
        </View>
      </KeyboardAvoidingView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#ffffff',
  },
  topBar: {
    // Floating-Overlay: schwebt über Chat, kein eigener Header-Block
    // top wird inline gesetzt (insets.top + 6) damit Avatar UNTER der Notch sitzt
    position: 'absolute',
    left: 0,
    right: 0,
    zIndex: 10,
    flexDirection: 'row',
    alignItems: 'flex-start',
    justifyContent: 'space-between',
    paddingHorizontal: 12,
    pointerEvents: 'box-none',
  },
  topBarBackdrop: {
    // Solider Hintergrund unter dem Floating-Avatar — Chat-Messages
    // scrollen darunter durch, werden aber nicht mehr sichtbar mit Lyra
    // Avatar/Name kollidieren.
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    zIndex: 9,
    backgroundColor: '#ffffff',
  },
  backBtn: {
    width: 40,
    height: 40,
    borderRadius: 20,
    backgroundColor: 'rgba(255,255,255,0.92)',
    alignItems: 'center',
    justifyContent: 'center',
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.08,
    shadowRadius: 6,
    elevation: 4,
  },
  avatarCenter: {
    flex: 1,
    alignItems: 'center',
    gap: 4,
  },
  avatarMeta: {
    alignItems: 'center',
    gap: 2,
  },
  avatarName: {
    fontSize: 15,
    fontFamily: 'Nunito_700Bold',
    color: '#0a0a0a',
  },
  statusLabel: {
    fontSize: 11,
    fontFamily: 'Nunito_400Regular',
    color: '#737373',
  },
  speakingRow: {
    flexDirection: 'row',
    alignItems: 'center',
    gap: 6,
  },
  speakingLabel: {
    fontSize: 11,
    fontFamily: 'Nunito_600SemiBold',
  },
  stopBtn: {
    width: 18,
    height: 18,
    borderRadius: 9,
    backgroundColor: '#f5f5f5',
    alignItems: 'center',
    justifyContent: 'center',
  },
  newChatBtn: {
    width: 40,
    height: 40,
    borderRadius: 20,
    backgroundColor: 'rgba(255,255,255,0.92)',
    alignItems: 'center',
    justifyContent: 'center',
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.08,
    shadowRadius: 6,
    elevation: 4,
  },
  listContent: {
    paddingHorizontal: 12,
    // Reserve Platz für den Floating-Avatar oben (Avatar 112px + Name + Status + Gaps + Margin)
    paddingTop: 210,
    paddingBottom: 12,
  },
  // Insta-DM row
  msgRow: {
    flexDirection: 'row',
    marginBottom: 4,
    alignItems: 'flex-end',
  },
  msgRowUser: {
    justifyContent: 'flex-end',
  },
  msgRowAssistant: {
    justifyContent: 'flex-start',
  },
  assistantAvatarSlot: {
    width: 28,
    marginRight: 6,
    alignItems: 'center',
    justifyContent: 'flex-end',
  },
  bubbleCol: {
    maxWidth: '75%',
    gap: 2,
  },
  bubbleColUser: {
    alignItems: 'flex-end',
  },
  bubbleColAssistant: {
    alignItems: 'flex-start',
  },
  bubble: {
    borderRadius: 20,
    paddingHorizontal: 14,
    paddingVertical: 9,
  },
  bubbleUser: {
    backgroundColor: '#007AFF',
    borderBottomRightRadius: 4,
  },
  bubbleAssistant: {
    backgroundColor: '#f0f0f0',
    borderBottomLeftRadius: 4,
  },
  bubbleText: {
    fontSize: 15,
    lineHeight: 21,
    fontFamily: 'Nunito_400Regular',
  },
  bubbleTextUser: {
    color: '#ffffff',
  },
  bubbleTextAssistant: {
    color: '#0a0a0a',
  },
  metaRow: {
    flexDirection: 'row',
    alignItems: 'center',
    gap: 4,
    paddingHorizontal: 4,
    marginBottom: 4,
  },
  metaRowUser: {
    justifyContent: 'flex-end',
  },
  metaRowAssistant: {
    justifyContent: 'flex-start',
  },
  feedbackText: {
    fontSize: 10,
    color: '#16a34a',
    fontFamily: 'Nunito_400Regular',
  },
  timestampText: {
    fontSize: 10,
    color: '#a3a3a3',
    fontFamily: 'Nunito_400Regular',
  },
  thinkingRow: {
    flexDirection: 'row',
    gap: 4,
    alignItems: 'center',
    paddingVertical: 4,
  },
  thinkingDot: {
    width: 8,
    height: 8,
    borderRadius: 4,
    backgroundColor: '#d4d4d4',
  },
  scrollDownBtn: {
    position: 'absolute',
    bottom: 12,
    right: 16,
    width: 36,
    height: 36,
    borderRadius: 18,
    backgroundColor: '#007AFF',
    alignItems: 'center',
    justifyContent: 'center',
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.2,
    shadowRadius: 4,
    elevation: 4,
  },
  inputBar: {
    flexDirection: 'row',
    alignItems: 'flex-end',
    gap: 8,
    paddingHorizontal: 12,
    paddingTop: 8,
    borderTopWidth: StyleSheet.hairlineWidth,
    borderTopColor: '#e5e5e5',
    backgroundColor: 'rgba(255,255,255,0.97)',
  },
  textInput: {
    flex: 1,
    backgroundColor: '#f5f5f5',
    borderRadius: 22,
    paddingVertical: 9,
    paddingHorizontal: 16,
    fontSize: 15,
    fontFamily: 'Nunito_400Regular',
    color: '#0a0a0a',
    maxHeight: 120,
  },
  micBtn: {
    width: 38,
    height: 38,
    borderRadius: 19,
    backgroundColor: '#f5f5f5',
    alignItems: 'center',
    justifyContent: 'center',
  },
  micBtnActive: {
    backgroundColor: '#dc2626',
    transform: [{ scale: 1.1 }],
  },
  micBtnDisabled: {
    opacity: 0.4,
  },
  sendBtn: {
    width: 38,
    height: 38,
    borderRadius: 19,
    backgroundColor: '#007AFF',
    alignItems: 'center',
    justifyContent: 'center',
  },
  sendBtnDisabled: {
    opacity: 0.4,
  },
  recordingContainer: {
    flex: 1,
    flexDirection: 'row',
    alignItems: 'center',
    gap: 8,
    backgroundColor: 'rgba(220,38,38,0.08)',
    borderWidth: 1,
    borderColor: 'rgba(220,38,38,0.2)',
    borderRadius: 22,
    paddingHorizontal: 12,
    paddingVertical: 8,
  },
  cancelBtn: {
    width: 32,
    height: 32,
    borderRadius: 16,
    backgroundColor: 'rgba(220,38,38,0.15)',
    alignItems: 'center',
    justifyContent: 'center',
  },
  pulseDot: {
    width: 8,
    height: 8,
    borderRadius: 4,
    backgroundColor: '#dc2626',
  },
  recordingTimer: {
    fontSize: 13,
    fontFamily: 'Nunito_600SemiBold',
    color: '#f87171',
    fontVariant: ['tabular-nums'],
  },
  transcribingRow: {
    flex: 1,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center',
    gap: 8,
    paddingVertical: 10,
  },
  transcribingText: {
    fontSize: 14,
    color: '#737373',
    fontFamily: 'Nunito_400Regular',
  },
});
