import { useState, useRef, useEffect, useCallback } from 'react';
import {
  View,
  Text,
  TextInput,
  FlatList,
  TouchableOpacity,
  Platform,
  Alert,
  ActivityIndicator,
  StyleSheet,
  Keyboard,
  Modal,
  ScrollView,
  Dimensions,
  type FlatList as FlatListType,
} from 'react-native';
import { KeyboardStickyView } from 'react-native-keyboard-controller';
import { Image } from 'expo-image';
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
import { useRouter, useLocalSearchParams, useFocusEffect } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import * as ImagePicker from 'expo-image-picker';
// 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 { apiFetch } from '../lib/api';
import { ChatBubble, type ChatMsg, type MessageReaction } from '../components/chat/ChatBubble';
import { DmChatBackground } from '../components/chat/DmChatBackground';
import { useDmRealtime } from '../hooks/useChatRealtime';
import { useColors } from '../lib/theme';
import { useThemeStore } from '../stores/theme';
import { useAuthStore } from '../stores/auth';
import { supabase } from '../lib/supabase';
import { UserAvatar } from '../components/UserAvatar';
import { ChatHeaderStatus } from '../components/chat/ChatHeaderStatus';

type DmHistoryResponse = {
  partner: {
    id: string;
    nickname: string;
    username?: string;
    avatar?: string | null;
  };
  messages: Array<{
    id: string;
    content: string;
    createdAt: string;
    isOwn: boolean;
    readAt: string | null;
    senderId?: string;
    receiverId?: string;
    likesCount?: number;
    likedByMe?: boolean;
    attachmentUrl?: string | null;
    attachmentType?: string | null;
    attachmentName?: string | null;
    replyTo?: any;
  }>;
};

const GROUP_GAP_MS = 5 * 60 * 1000;

export default function DmScreen() {
  const { t } = useTranslation();
  const router = useRouter();
  const insets = useSafeAreaInsets();
  const isAndroid = Platform.OS === 'android';
  const colors = useColors();
  const styles = makeStyles(colors);
  const queryClient = useQueryClient();
  const myUserId = useAuthStore((s) => s.user?.id);

  const colorScheme = useThemeStore((s) => s.colorScheme);
  const chatBg = colorScheme === 'dark' ? '#1a1f1e' : '#EDE8E1';

  const { userId } = useLocalSearchParams<{ userId: string }>();

  const flatListRef = useRef<FlatListType<ChatMsg>>(null);
  const [messages, setMessages] = useState<ChatMsg[]>([]);
  const [partner, setPartner] = useState<DmHistoryResponse['partner'] | null>(null);
  const partnerRef = useRef<DmHistoryResponse['partner'] | null>(null);
  const [replyTo, setReplyTo] = useState<{ id: string; nickname: string; content: string } | null>(
    null,
  );
  const [sending, setSending] = useState(false);
  const [inputText, setInputText] = useState('');
  const [attachment, setAttachment] = useState<{ uri: string; name: string } | null>(null);
  const [uploading, setUploading] = useState(false);
  const [keyboardVisible, setKeyboardVisible] = useState(false);
  const [keyboardHeight, setKeyboardHeight] = useState(0);
  const [inputBarHeight, setInputBarHeight] = useState(60);
  const [infoSheetOpen, setInfoSheetOpen] = useState(false);
  const [lightboxUri, setLightboxUri] = useState<string | null>(null);

  // Reset aller conversation-spezifischen States wenn userId wechselt (Stack-Reuse)
  useEffect(() => {
    setMessages([]);
    setPartner(null);
    partnerRef.current = null;
    setReplyTo(null);
  }, [userId]);

  // Keyboard-Sichtbarkeit tracken + scroll to end beim Schließen
  useEffect(() => {
    const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
    const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
    const show = Keyboard.addListener(showEvent, (e) => {
      setKeyboardHeight(e.endCoordinates.height);
      setKeyboardVisible(true);
      requestAnimationFrame(() => flatListRef.current?.scrollToEnd({ animated: false }));
    });
    const hide = Keyboard.addListener(hideEvent, () => {
      setKeyboardHeight(0);
      setKeyboardVisible(false);
      setTimeout(() => flatListRef.current?.scrollToEnd({ animated: false }), 50);
    });
    return () => { show.remove(); hide.remove(); };
  }, []);

  // Wenn User zurücknavigiert, soll die Conversation-Liste sofort neu laden
  // (unread-Badge soll verschwinden — Backend hat bereits markDmsAsRead beim GET aufgerufen)
  useFocusEffect(
    useCallback(() => {
      return () => {
        queryClient.invalidateQueries({ queryKey: ['dm-conversations'] });
      };
    }, [queryClient]),
  );

  // Lade DM-History — staleTime:0 erzwingt immer frischen Fetch (kein Cache-Hit-Bug)
  const { isLoading, isFetching } = useQuery({
    queryKey: ['dm-history', userId],
    queryFn: async () => {
      console.log('[dm] fetching history for partner', userId, 'me', myUserId);
      try {
        const data = await apiFetch<DmHistoryResponse>(`/api/chat/dm/${userId}`);
        console.log('[dm] partner:', data.partner?.nickname, 'msgs:', data.messages?.length);
        setPartner(data.partner);
        partnerRef.current = data.partner;
      const msgs: ChatMsg[] = data.messages.map((m: any) => ({
        id: m.id,
        userId: m.senderId ?? (m.isOwn ? myUserId ?? '' : userId),
        nickname: m.isOwn ? 'Du' : data.partner?.nickname ?? '?',
        avatar: m.isOwn ? null : data.partner?.avatar ?? null,
        content: m.content,
        replyTo: m.replyTo
          ? {
              id: m.replyTo.id,
              userId: m.replyTo.senderId,
              nickname:
                m.replyTo.senderId === myUserId ? 'Du' : data.partner?.nickname ?? '?',
              content: m.replyTo.content?.slice(0, 100) ?? '',
              attachmentType: m.replyTo.attachmentType ?? null,
            }
          : null,
        attachmentUrl: m.attachmentUrl ?? null,
        attachmentType: m.attachmentType ?? null,
        attachmentName: m.attachmentName ?? null,
        likesCount: m.likesCount ?? 0,
        likedByMe: m.likedByMe ?? false,
        createdAt: m.createdAt,
        isOwn: m.isOwn,
        readAt: m.readAt,
        reactions: m.reactions ?? [],
        deleted: m.deleted ?? false,
      }));
      setMessages(msgs);
      // Dreistufiges Scroll-to-bottom: rAF + 100ms + 300ms deckt
      // Fälle ab wo Bilder nachgeladen werden und Content-Höhe wächst.
      requestAnimationFrame(() => flatListRef.current?.scrollToEnd({ animated: false }));
      setTimeout(() => flatListRef.current?.scrollToEnd({ animated: false }), 100);
      setTimeout(() => flatListRef.current?.scrollToEnd({ animated: false }), 300);
      return data;
      } catch (err: any) {
        console.error('[dm] history fetch failed:', err?.message ?? err);
        throw err;
      }
    },
    enabled: !!userId && !!myUserId,
    staleTime: 0,
    gcTime: 0,
  });

  // Neue Nachricht (incoming Realtime oder outgoing send) → immer scrollen
  useEffect(() => {
    if (messages.length === 0) return;
    requestAnimationFrame(() => flatListRef.current?.scrollToEnd({ animated: true }));
  }, [messages.length]);

  // Realtime: neue DMs vom Partner
  const onDmInsert = useCallback(
    (row: any) => {
      if (row.receiver_id !== myUserId) return;
      setMessages((prev) => {
        if (prev.some((m) => m.id === row.id)) return prev;
        const p = partnerRef.current;
        return [
          ...prev,
          {
            id: row.id,
            userId: row.sender_id,
            nickname: p?.nickname ?? '?',
            avatar: p?.avatar ?? null,
            content: row.content ?? '',
            replyTo: null,
            attachmentUrl: row.attachment_url ?? null,
            attachmentType: row.attachment_type ?? null,
            attachmentName: row.attachment_name ?? null,
            likesCount: row.likes_count ?? 0,
            likedByMe: false,
            createdAt: row.created_at,
            isOwn: false,
            readAt: null,
          },
        ];
      });
    },
    [myUserId],
  );
  // Realtime: Partner-Soft-Delete (Tombstone) + Reaktions-Änderungen → refetch.
  const refetchHistory = useCallback(() => {
    queryClient.invalidateQueries({ queryKey: ['dm-history', userId] });
  }, [queryClient, userId]);
  useDmRealtime(userId, onDmInsert, !!myUserId, refetchHistory, refetchHistory);

  async function pickImage() {
    const perm = await ImagePicker.requestMediaLibraryPermissionsAsync();
    if (!perm.granted) {
      Alert.alert(t('chat.photo_access_title'), t('chat.photo_access_body'));
      return;
    }
    const result = await ImagePicker.launchImageLibraryAsync({
      mediaTypes: ImagePicker.MediaTypeOptions.Images,
      quality: 0.8,
    });
    if (!result.canceled && result.assets[0]?.uri) {
      const a = result.assets[0];
      setAttachment({ uri: a.uri, name: a.fileName ?? `image-${Date.now()}.jpg` });
    }
  }

  async function uploadAttachment(file: { uri: string; name: string }): Promise<{ url: string; type: string; name: string } | null> {
    try {
      setUploading(true);
      const ext = file.name.split('.').pop() || 'jpg';
      const path = `chat/${Date.now()}_${Math.random().toString(36).slice(2, 8)}.${ext}`;
      const base64 = await FileSystem.readAsStringAsync(file.uri, {
        encoding: FileSystem.EncodingType.Base64,
      });
      const binary = typeof atob === 'function' ? atob(base64) : Buffer.from(base64, 'base64').toString('binary');
      const bytes = new Uint8Array(binary.length);
      for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
      const { error } = await supabase.storage.from('chat-attachments').upload(path, bytes, {
        cacheControl: '3600',
        upsert: false,
        contentType: 'image/jpeg',
      });
      if (error) throw error;
      const { data } = supabase.storage.from('chat-attachments').getPublicUrl(path);
      return { url: data.publicUrl, type: 'image', name: file.name };
    } catch (err: any) {
      Alert.alert(t('chat.upload_failed'), err?.message ?? '');
      return null;
    } finally {
      setUploading(false);
    }
  }

  async function handleSend() {
    const content = inputText.trim();
    if (!content && !attachment) return;
    if (sending || uploading) return;

    const tempId = `temp-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
    const currentReplyTo = replyTo;
    const currentAttachment = attachment;

    const optimisticMsg: ChatMsg = {
      id: tempId,
      userId: myUserId ?? '',
      nickname: 'Du',
      avatar: null,
      content,
      replyTo: currentReplyTo
        ? {
            id: currentReplyTo.id,
            userId: myUserId ?? '',
            nickname: currentReplyTo.nickname,
            content: currentReplyTo.content,
            attachmentType: null,
          }
        : null,
      attachmentUrl: currentAttachment?.uri ?? null,
      attachmentType: currentAttachment ? 'image' : null,
      attachmentName: currentAttachment?.name ?? null,
      likesCount: 0,
      likedByMe: false,
      createdAt: new Date().toISOString(),
      isOwn: true,
      readAt: null,
      status: 'pending',
    };

    setMessages((prev) => [...prev, optimisticMsg]);
    setInputText('');
    setAttachment(null);
    setReplyTo(null);
    setSending(true);

    try {
      let attachmentMeta: { url: string; type: string; name: string } | null = null;
      if (currentAttachment) {
        attachmentMeta = await uploadAttachment(currentAttachment);
        if (!attachmentMeta) {
          setMessages((prev) =>
            prev.map((m) => (m.id === tempId ? { ...m, status: 'failed' as const } : m)),
          );
          setSending(false);
          return;
        }
      }

      const newMsg = await apiFetch<any>('/api/chat/dm', {
        method: 'POST',
        body: {
          receiverId: userId,
          content,
          replyToId: currentReplyTo?.id,
          attachmentUrl: attachmentMeta?.url,
          attachmentType: attachmentMeta?.type,
          attachmentName: attachmentMeta?.name,
        },
      });

      setMessages((prev) =>
        prev.map((m) =>
          m.id === tempId
            ? {
                ...m,
                id: newMsg.id,
                content: newMsg.content,
                attachmentUrl: newMsg.attachmentUrl ?? null,
                attachmentType: newMsg.attachmentType ?? null,
                attachmentName: newMsg.attachmentName ?? null,
                createdAt: newMsg.createdAt,
                status: 'sent' as const,
              }
            : m,
        ),
      );

      queryClient.invalidateQueries({ queryKey: ['dm-conversations'] });
    } catch (err) {
      console.error('DM send failed:', err);
      setMessages((prev) =>
        prev.map((m) => (m.id === tempId ? { ...m, status: 'failed' as const } : m)),
      );
    } finally {
      setSending(false);
    }
  }

  async function toggleLike(msg: ChatMsg) {
    try {
      const { liked } = await apiFetch<{ liked: boolean }>('/api/chat/like', {
        method: 'POST',
        body: { messageId: msg.id, type: 'dm' },
      });
      setMessages((prev) =>
        prev.map((m) =>
          m.id === msg.id
            ? { ...m, likedByMe: liked, likesCount: m.likesCount + (liked ? 1 : -1) }
            : m,
        ),
      );
    } catch {}
  }

  // Optimistisches Anwenden einer Reaktion auf die aggregierte Pills-Liste
  // (WhatsApp-Toggle: gleiches Emoji entfernt, anderes ersetzt meine Reaktion).
  function applyReactionOptimistic(
    reactions: MessageReaction[],
    emoji: string,
  ): MessageReaction[] {
    const list = reactions.map((r) => ({ ...r }));
    const mine = list.find((r) => r.mine);
    const toggledOff = mine?.emoji === emoji;
    if (mine) {
      mine.count -= 1;
      mine.mine = false;
    }
    const cleaned = list.filter((r) => r.count > 0);
    if (toggledOff) return cleaned;
    const existing = cleaned.find((r) => r.emoji === emoji);
    if (existing) {
      existing.count += 1;
      existing.mine = true;
    } else {
      cleaned.push({ emoji, count: 1, mine: true });
    }
    return cleaned;
  }

  async function toggleReaction(msg: ChatMsg, emoji: string) {
    setMessages((prev) =>
      prev.map((m) =>
        m.id === msg.id
          ? { ...m, reactions: applyReactionOptimistic(m.reactions ?? [], emoji) }
          : m,
      ),
    );
    try {
      await apiFetch('/api/chat/reaction', {
        method: 'POST',
        body: { messageId: msg.id, emoji },
      });
    } catch {
      refetchHistory(); // Rollback via Server-State
    }
  }

  function deleteMessage(msg: ChatMsg) {
    Alert.alert(
      t('chat.delete_confirm_title'),
      t('chat.delete_confirm_msg'),
      [
        { text: t('common.cancel'), style: 'cancel' },
        {
          text: t('chat.delete'),
          style: 'destructive',
          onPress: async () => {
            setMessages((prev) =>
              prev.map((m) =>
                m.id === msg.id
                  ? { ...m, deleted: true, content: '', attachmentUrl: null, attachmentType: null, reactions: [] }
                  : m,
              ),
            );
            try {
              await apiFetch('/api/chat/delete-message', {
                method: 'POST',
                body: { messageId: msg.id },
              });
            } catch {
              refetchHistory();
            }
          },
        },
      ],
    );
  }

  function startReply(msg: ChatMsg) {
    setReplyTo({
      id: msg.id,
      nickname: msg.nickname ?? '?',
      content: msg.content?.slice(0, 100) || (msg.attachmentType === 'image' ? 'Bild' : ''),
    });
  }

  function sameAuthor(a: ChatMsg | undefined, b: ChatMsg | undefined): boolean {
    if (!a || !b) return false;
    if (a.userId !== b.userId) return false;
    return Math.abs(new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) <= GROUP_GAP_MS;
  }

  return (
    <SafeAreaView style={styles.container} edges={['top']}>
      <View style={[styles.header, { backgroundColor: colors.bg }]}>
        <TouchableOpacity style={styles.backBtn} onPress={() => router.back()} hitSlop={8} activeOpacity={0.7}>
          <Ionicons name="chevron-back" size={22} color={colors.text} />
        </TouchableOpacity>

        {/* Avatar + Name — tap → Profil */}
        <TouchableOpacity
          style={styles.headerCenter}
          activeOpacity={0.7}
          onPress={() => userId && router.push(`/profile/${userId}` as any)}
        >
          <View style={{ marginRight: 8 }}>
            <UserAvatar
              userId={userId ?? null}
              avatar={partner?.avatar ?? null}
              nickname={partner?.nickname ?? '?'}
              size="md"
            />
          </View>
          <View style={{ flexShrink: 1 }}>
            <Text style={styles.headerName} numberOfLines={1}>
              {partner?.nickname ?? '…'}
            </Text>
            {userId && <ChatHeaderStatus userId={userId} />}
          </View>
        </TouchableOpacity>

        {/* Info-Button */}
        <TouchableOpacity
          style={styles.infoBtn}
          hitSlop={8}
          activeOpacity={0.7}
          onPress={() => setInfoSheetOpen(true)}
        >
          <Ionicons name="information-circle-outline" size={24} color={colors.text} />
        </TouchableOpacity>
      </View>

      <View style={{ flex: 1, backgroundColor: chatBg }}>
        <DmChatBackground />
        {(isLoading || isFetching) && messages.length === 0 ? (
          <View style={styles.loadingBox}>
            <ActivityIndicator color={colors.brandOrange} />
          </View>
        ) : messages.length === 0 ? (
          <View style={styles.loadingBox}>
            <Ionicons name="chatbubble-outline" size={42} color="#d4d4d4" />
            <Text style={styles.emptyText}>{t('chat.no_chats')}</Text>
          </View>
        ) : (
          <FlatList
            ref={flatListRef}
            data={messages}
            style={{ flex: 1 }}
            renderItem={({ item, index }) => (
              <ChatBubble
                msg={item}
                isDM
                isFirstInGroup={!sameAuthor(messages[index - 1], item)}
                isLastInGroup={!sameAuthor(item, messages[index + 1])}
                onReply={startReply}
                onLike={toggleLike}
                onReact={toggleReaction}
                onDelete={deleteMessage}
                onOpenImage={() => {}}
              />
            )}
            keyExtractor={(m) => m.id}
            contentContainerStyle={{
              paddingHorizontal: 0,
              paddingTop: 12,
              paddingBottom: inputBarHeight + 12,
            }}
            showsVerticalScrollIndicator={false}
            keyboardDismissMode="interactive"
            keyboardShouldPersistTaps="handled"
            onContentSizeChange={() => flatListRef.current?.scrollToEnd({ animated: false })}
          />
        )}
      </View>

      <KeyboardStickyView
        offset={{ closed: -insets.bottom, opened: 0 }}
        style={{ backgroundColor: colors.bg }}
      >
        <View
          onLayout={(e) => {
            const h = e.nativeEvent.layout.height;
            if (Math.abs(h - inputBarHeight) > 1) setInputBarHeight(h);
          }}
          style={[
            styles.inputBar,
            {
              paddingBottom: 8,
              backgroundColor: colors.bg,
              borderTopColor: colors.border,
            },
          ]}
        >
          {replyTo && (
            <View style={[styles.replyBar, { backgroundColor: colors.surface }]}>
              <Ionicons name="arrow-undo" size={14} color="#007AFF" style={{ marginRight: 6 }} />
              <View style={{ flex: 1 }}>
                <Text style={styles.replyName} numberOfLines={1}>
                  {t('chat.reply_to')} {replyTo.nickname}
                </Text>
                <Text style={[styles.replyContent, { color: colors.textMuted }]} numberOfLines={1}>
                  {replyTo.content || '…'}
                </Text>
              </View>
              <TouchableOpacity hitSlop={10} onPress={() => setReplyTo(null)} activeOpacity={0.7}>
                <Ionicons name="close" size={16} color={colors.textMuted} />
              </TouchableOpacity>
            </View>
          )}
          {attachment && (
            <View style={[styles.attachBar, { backgroundColor: colors.surface }]}>
              <Image source={{ uri: attachment.uri }} style={styles.attachImg} contentFit="cover" />
              <Text style={[styles.attachName, { color: colors.text }]} numberOfLines={1}>
                {attachment.name}
              </Text>
              <TouchableOpacity hitSlop={10} onPress={() => setAttachment(null)} activeOpacity={0.7}>
                <Ionicons name="close" size={16} color={colors.textMuted} />
              </TouchableOpacity>
            </View>
          )}
          <View style={styles.inputRow}>
            <TouchableOpacity
              activeOpacity={0.7}
              style={[styles.addBtn, { backgroundColor: colors.surfaceElevated }]}
              onPress={pickImage}
              disabled={uploading || sending}
            >
              <Ionicons name="add" size={22} color={colors.textMuted} />
            </TouchableOpacity>
            <TextInput
              style={[styles.textInput, { backgroundColor: colors.surfaceElevated, color: colors.text }]}
              placeholder={t('chat.placeholder')}
              placeholderTextColor={colors.textMuted}
              value={inputText}
              onChangeText={setInputText}
              multiline
              maxLength={2000}
              returnKeyType="send"
              onSubmitEditing={handleSend}
              editable={!sending && !uploading}
            />
            {(inputText.trim().length > 0 || attachment) && (
              <TouchableOpacity
                style={[styles.sendBtn, (sending || uploading) && styles.sendBtnDisabled]}
                onPress={handleSend}
                disabled={sending || uploading}
                activeOpacity={0.7}
              >
                {sending || uploading ? (
                  <ActivityIndicator size="small" color="#fff" />
                ) : (
                  <Ionicons name="send" size={16} color="#fff" />
                )}
              </TouchableOpacity>
            )}
          </View>
        </View>
      </KeyboardStickyView>

      {/* ── Info-Sheet ─────────────────────────────────────────────── */}
      <DmInfoSheet
        visible={infoSheetOpen}
        onClose={() => setInfoSheetOpen(false)}
        partner={partner}
        messages={messages}
        onImagePress={(uri) => setLightboxUri(uri)}
        onViewProfile={() => {
          setInfoSheetOpen(false);
          setTimeout(() => userId && router.push(`/profile/${userId}` as any), 250);
        }}
        colors={colors}
        t={t}
      />

      {/* ── Lightbox ───────────────────────────────────────────────── */}
      <Modal visible={!!lightboxUri} transparent animationType="fade" onRequestClose={() => setLightboxUri(null)}>
        <TouchableOpacity
          style={{ flex: 1, backgroundColor: 'rgba(0,0,0,0.92)', alignItems: 'center', justifyContent: 'center' }}
          activeOpacity={1}
          onPress={() => setLightboxUri(null)}
        >
          {lightboxUri && (
            <Image
              source={{ uri: lightboxUri }}
              style={{ width: Dimensions.get('window').width, height: Dimensions.get('window').width }}
              contentFit="contain"
              cachePolicy="memory-disk"
            />
          )}
          <TouchableOpacity
            style={{ position: 'absolute', top: 54, right: 20, padding: 8 }}
            onPress={() => setLightboxUri(null)}
            activeOpacity={0.7}
          >
            <Ionicons name="close-circle" size={32} color="#fff" />
          </TouchableOpacity>
        </TouchableOpacity>
      </Modal>
    </SafeAreaView>
  );
}

// ─── DmInfoSheet ─────────────────────────────────────────────────────────────

const MEDIA_COL = 3;
const MEDIA_GAP = 2;
const MEDIA_SIZE = (Dimensions.get('window').width - MEDIA_GAP * (MEDIA_COL + 1)) / MEDIA_COL;

function DmInfoSheet({
  visible,
  onClose,
  partner,
  messages,
  onImagePress,
  onViewProfile,
  colors,
  t,
}: {
  visible: boolean;
  onClose: () => void;
  partner: { id: string; nickname: string; avatar?: string | null } | null;
  messages: ChatMsg[];
  onImagePress: (uri: string) => void;
  onViewProfile: () => void;
  colors: ReturnType<typeof useColors>;
  t: ReturnType<typeof import('react-i18next').useTranslation>['t'];
}) {
  const sharedMedia = messages.filter(
    (m) => m.attachmentType === 'image' && m.attachmentUrl,
  );

  return (
    <Modal visible={visible} animationType="slide" presentationStyle="pageSheet" onRequestClose={onClose}>
      <View style={{ flex: 1, backgroundColor: colors.bg }}>
        {/* Header */}
        <View style={{
          flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16,
          paddingTop: 16, paddingBottom: 12,
          borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: colors.border,
        }}>
          <Text style={{ flex: 1, fontSize: 17, fontFamily: 'Nunito_700Bold', color: colors.text }}>
            {t('chat.info')}
          </Text>
          <TouchableOpacity onPress={onClose} hitSlop={8} activeOpacity={0.7}>
            <Ionicons name="close" size={24} color={colors.text} />
          </TouchableOpacity>
        </View>

        <ScrollView contentContainerStyle={{ paddingBottom: 40 }}>
          {/* Partner-Karte */}
          <TouchableOpacity
            activeOpacity={0.7}
            onPress={onViewProfile}
            style={{
              flexDirection: 'row', alignItems: 'center',
              padding: 16, gap: 14,
            }}
          >
            {partner?.avatar ? (
              <Image
                source={{ uri: partner.avatar }}
                style={{ width: 56, height: 56, borderRadius: 28 }}
                contentFit="cover"
                cachePolicy="memory-disk"
              />
            ) : (
              <View style={{
                width: 56, height: 56, borderRadius: 28,
                backgroundColor: colors.brandOrange + '30',
                alignItems: 'center', justifyContent: 'center',
              }}>
                <Text style={{ fontSize: 20, fontFamily: 'Nunito_700Bold', color: colors.brandOrange }}>
                  {partner?.nickname?.[0]?.toUpperCase() ?? '?'}
                </Text>
              </View>
            )}
            <View style={{ flex: 1 }}>
              <Text style={{ fontSize: 17, fontFamily: 'Nunito_700Bold', color: colors.text }}>
                {partner?.nickname ?? '…'}
              </Text>
              <Text style={{ fontSize: 13, fontFamily: 'Nunito_400Regular', color: colors.textMuted, marginTop: 2 }}>
                {t('dm.view_profile', { defaultValue: 'Profil anzeigen' })}
              </Text>
            </View>
            <Ionicons name="chevron-forward" size={18} color={colors.textMuted} />
          </TouchableOpacity>

          <View style={{ height: StyleSheet.hairlineWidth, backgroundColor: colors.border, marginHorizontal: 16 }} />

          {/* Geteilte Medien */}
          <View style={{ paddingHorizontal: 16, paddingTop: 20, paddingBottom: 12 }}>
            <Text style={{ fontSize: 15, fontFamily: 'Nunito_700Bold', color: colors.text }}>
              {t('dm.shared_media', { defaultValue: 'Geteilte Medien' })}
              {sharedMedia.length > 0 && (
                <Text style={{ fontSize: 13, fontFamily: 'Nunito_400Regular', color: colors.textMuted }}>
                  {'  '}{sharedMedia.length}
                </Text>
              )}
            </Text>
          </View>

          {sharedMedia.length === 0 ? (
            <View style={{ alignItems: 'center', paddingVertical: 32 }}>
              <Ionicons name="images-outline" size={40} color={colors.textMuted} />
              <Text style={{ marginTop: 10, fontSize: 13, fontFamily: 'Nunito_400Regular', color: colors.textMuted }}>
                {t('dm.no_shared_media', { defaultValue: 'Keine geteilten Medien' })}
              </Text>
            </View>
          ) : (
            <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: MEDIA_GAP, paddingHorizontal: MEDIA_GAP }}>
              {[...sharedMedia].reverse().map((m) => (
                <TouchableOpacity
                  key={m.id}
                  activeOpacity={0.8}
                  onPress={() => onImagePress(m.attachmentUrl!)}
                >
                  <Image
                    source={{ uri: m.attachmentUrl! }}
                    style={{ width: MEDIA_SIZE, height: MEDIA_SIZE }}
                    contentFit="cover"
                    cachePolicy="memory-disk"
                  />
                </TouchableOpacity>
              ))}
            </View>
          )}
        </ScrollView>
      </View>
    </Modal>
  );
}

function makeStyles(colors: ReturnType<typeof useColors>) {
  return StyleSheet.create({
    container: { flex: 1, backgroundColor: colors.bg },
    header: {
      flexDirection: 'row',
      alignItems: 'center',
      paddingHorizontal: 12,
      paddingVertical: 10,
    },
    backBtn: {
      padding: 8,
      alignItems: 'center',
      justifyContent: 'center',
    },
    headerCenter: {
      flex: 1,
      flexDirection: 'row',
      alignItems: 'center',
      marginLeft: 8,
    },
    infoBtn: {
      padding: 8,
      alignItems: 'center',
      justifyContent: 'center',
    },
    headerName: {
      fontSize: 15,
      fontFamily: 'Nunito_700Bold',
      color: colors.text,
      flexShrink: 1,
    },
    loadingBox: {
      flex: 1,
      alignItems: 'center',
      justifyContent: 'center',
    },
    emptyText: {
      fontSize: 13,
      fontFamily: 'Nunito_600SemiBold',
      color: colors.textMuted,
      marginTop: 12,
    },
    inputBar: {
      borderTopWidth: StyleSheet.hairlineWidth,
    },
    replyBar: {
      flexDirection: 'row',
      alignItems: 'center',
      paddingHorizontal: 12,
      paddingVertical: 8,
      borderLeftWidth: 3,
      borderLeftColor: '#007AFF',
      marginHorizontal: 8,
      marginTop: 6,
      borderRadius: 8,
    },
    replyName: {
      fontSize: 11,
      fontFamily: 'Nunito_700Bold',
      color: '#007AFF',
    },
    replyContent: {
      fontSize: 11,
      fontFamily: 'Nunito_400Regular',
      marginTop: 1,
    },
    attachBar: {
      flexDirection: 'row',
      alignItems: 'center',
      paddingHorizontal: 12,
      paddingVertical: 6,
      marginHorizontal: 8,
      marginTop: 6,
      borderRadius: 8,
    },
    attachImg: {
      width: 36,
      height: 36,
      borderRadius: 6,
      marginRight: 8,
    },
    attachName: {
      flex: 1,
      fontSize: 12,
      fontFamily: 'Nunito_600SemiBold',
    },
    inputRow: {
      flexDirection: 'row',
      alignItems: 'flex-end',
      gap: 8,
      paddingHorizontal: 12,
      paddingTop: 8,
    },
    addBtn: {
      width: 38,
      height: 38,
      borderRadius: 19,
      alignItems: 'center',
      justifyContent: 'center',
    },
    textInput: {
      flex: 1,
      borderRadius: 22,
      paddingVertical: 9,
      paddingHorizontal: 16,
      fontSize: 15,
      fontFamily: 'Nunito_400Regular',
      maxHeight: 120,
    },
    sendBtn: {
      width: 38,
      height: 38,
      borderRadius: 19,
      backgroundColor: '#007AFF',
      alignItems: 'center',
      justifyContent: 'center',
    },
    sendBtnDisabled: {
      opacity: 0.4,
    },
  });
}
