import { chat } from '@chat-app/shared';
import type {
  ConversationSummary,
  DecryptedMessage,
  MessageReaction,
} from '@chat-app/shared/chat';
import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
  ActionSheetIOS,
  ActivityIndicator,
  Alert,
  FlatList,
  KeyboardAvoidingView,
  Platform,
  Pressable,
  StyleSheet,
  Text,
  TextInput,
  View,
} from 'react-native';

import { MessageActionsSheet } from '../../../components/MessageActionsSheet';
import { MessageBubble } from '../../../components/MessageBubble';
import { useAuth } from '../../../lib/authContext';
import { useCall } from '../../../lib/callContext';
import { captureFromCamera, pickFromLibrary, type PickedImage } from '../../../lib/imagePicker';
import { supabase } from '../../../lib/supabase';
import { colors } from '../../../theme/colors';

export default function ConversationDetail() {
  const { id } = useLocalSearchParams<{ id: string }>();
  const { user, device, ownPrivateKey } = useAuth();
  const { state: callState, startCall } = useCall();
  const router = useRouter();

  // When a call moves into `connected`, jump to the in-call screen so
  // the user can see participants + controls. The /call screen pops
  // itself when callState returns to `idle`.
  useEffect(() => {
    if (callState.kind === 'connected') {
      router.push('/(app)/call');
    }
  }, [callState.kind, router]);

  const [conversation, setConversation] = useState<ConversationSummary | null>(null);
  const [messages, setMessages] = useState<DecryptedMessage[] | null>(null);
  const [reactions, setReactions] = useState<Map<string, MessageReaction[]>>(new Map());
  const [text, setText] = useState('');
  const [sending, setSending] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [replyTo, setReplyTo] = useState<DecryptedMessage | null>(null);
  const [activeMessage, setActiveMessage] = useState<DecryptedMessage | null>(null);
  const listRef = useRef<FlatList<DecryptedMessage>>(null);

  const load = useCallback(async () => {
    if (!id || !device || !ownPrivateKey) return;
    setError(null);
    try {
      const all = await chat.listConversations(supabase);
      setConversation(all.find((c) => c.id === id) ?? null);
      const ciphers = await chat.fetchConversationMessages(supabase, id, 50);
      const decrypted = await chat.decryptMessages({
        client: supabase,
        messages: ciphers,
        ownDeviceId: device.id,
        ownPrivateKey,
      });
      setMessages(decrypted);
      const rows = await chat.listReactionsForMessages(
        supabase,
        decrypted.map((m) => m.id),
      );
      const map = new Map<string, MessageReaction[]>();
      for (const r of rows) {
        const arr = map.get(r.messageId) ?? [];
        arr.push(r);
        map.set(r.messageId, arr);
      }
      setReactions(map);
    } catch (err: unknown) {
      setError(err instanceof Error ? err.message : 'Nachrichten konnten nicht geladen werden');
    }
  }, [id, device, ownPrivateKey]);

  useEffect(() => {
    void load();
  }, [load]);

  const members = useMemo(() => conversation?.members ?? [], [conversation]);
  const senderName = useCallback(
    (senderId: string) => {
      if (senderId === user?.id) return 'Du';
      const m = members.find((mm) => mm.userId === senderId);
      return m?.profile?.displayName ?? m?.profile?.username ?? 'Unbekannt';
    },
    [members, user],
  );

  const title =
    conversation?.type === 'group'
      ? (conversation.name ?? 'Gruppe')
      : (conversation?.peer?.displayName ?? '…');

  async function handleSendText() {
    if (!text.trim() || !user || !device || !ownPrivateKey || !id || sending) return;
    setSending(true);
    setError(null);
    const replyToId = replyTo?.id;
    try {
      await chat.sendEncryptedMessage({
        client: supabase,
        conversationId: id,
        plaintext: text.trim(),
        senderUserId: user.id,
        senderDeviceId: device.id,
        senderPrivateKey: ownPrivateKey,
        ...(replyToId ? { replyToId } : {}),
      });
      setText('');
      setReplyTo(null);
      await load();
    } catch (err: unknown) {
      setError(err instanceof Error ? err.message : 'Senden fehlgeschlagen');
    } finally {
      setSending(false);
    }
  }

  async function sendImage(pick: PickedImage) {
    if (!user || !device || !ownPrivateKey || !id) return;
    setSending(true);
    setError(null);
    try {
      const resp = await fetch(pick.uri);
      const blob = await resp.blob();
      const result = await chat.encryptAndUploadAttachment({
        client: supabase,
        conversationId: id,
        file: blob,
        mimeType: pick.mimeType,
        sizeBytes: pick.sizeBytes,
        width: pick.width,
        height: pick.height,
      });
      await chat.sendEncryptedMessage({
        client: supabase,
        conversationId: id,
        plaintext: '',
        senderUserId: user.id,
        senderDeviceId: device.id,
        senderPrivateKey: ownPrivateKey,
        attachmentHandles: [result.handle],
      });
      await load();
    } catch (err: unknown) {
      setError(err instanceof Error ? err.message : 'Bild senden fehlgeschlagen');
    } finally {
      setSending(false);
    }
  }

  function openImageMenu() {
    if (Platform.OS === 'ios') {
      ActionSheetIOS.showActionSheetWithOptions(
        { options: ['Foto aufnehmen', 'Aus Galerie wählen', 'Abbrechen'], cancelButtonIndex: 2 },
        async (idx) => {
          if (idx === 0) {
            const p = await captureFromCamera();
            if (p) await sendImage(p);
          } else if (idx === 1) {
            const p = await pickFromLibrary();
            if (p) await sendImage(p);
          }
        },
      );
    } else {
      Alert.alert('Bild senden', undefined, [
        {
          text: 'Foto aufnehmen',
          onPress: async () => {
            const p = await captureFromCamera();
            if (p) await sendImage(p);
          },
        },
        {
          text: 'Aus Galerie wählen',
          onPress: async () => {
            const p = await pickFromLibrary();
            if (p) await sendImage(p);
          },
        },
        { text: 'Abbrechen', style: 'cancel' },
      ]);
    }
  }

  async function handleReact(emoji: string, messageId: string) {
    if (!user) return;
    try {
      const mine = reactions.get(messageId)?.some((r) => r.userId === user.id && r.emoji === emoji);
      if (mine) {
        await chat.removeReaction(supabase, messageId, emoji);
      } else {
        await chat.addReaction(supabase, messageId, emoji);
      }
      const rows = await chat.listReactionsForMessages(
        supabase,
        (messages ?? []).map((m) => m.id),
      );
      const map = new Map<string, MessageReaction[]>();
      for (const r of rows) {
        const arr = map.get(r.messageId) ?? [];
        arr.push(r);
        map.set(r.messageId, arr);
      }
      setReactions(map);
    } catch (err: unknown) {
      setError(err instanceof Error ? err.message : 'Reaktion fehlgeschlagen');
    }
  }

  function handleDelete(messageId: string) {
    Alert.alert('Nachricht löschen', 'Diese Nachricht für alle löschen?', [
      { text: 'Abbrechen', style: 'cancel' },
      {
        text: 'Löschen',
        style: 'destructive',
        onPress: async () => {
          try {
            await chat.softDeleteMessage(supabase, messageId);
            await load();
          } catch (err: unknown) {
            setError(err instanceof Error ? err.message : 'Löschen fehlgeschlagen');
          }
        },
      },
    ]);
  }

  const parentLookup = useMemo(() => {
    const m = new Map<string, DecryptedMessage>();
    for (const msg of messages ?? []) m.set(msg.id, msg);
    return m;
  }, [messages]);

  return (
    <KeyboardAvoidingView
      style={styles.container}
      behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
      keyboardVerticalOffset={Platform.OS === 'ios' ? 80 : 0}
    >
      <Stack.Screen
        options={{
          title,
          headerStyle: { backgroundColor: colors.bg },
          headerTitleStyle: { color: colors.text },
          headerBackTitle: 'Chats',
          headerRight: () => (
            <Pressable
              onPress={() => {
                if (!id) return;
                void startCall(id);
              }}
              hitSlop={10}
              style={styles.callBtn}
            >
              <Text style={styles.callBtnText}>📞</Text>
            </Pressable>
          ),
        }}
      />

      {messages === null && !error && (
        <View style={styles.loading}>
          <ActivityIndicator color={colors.accent} />
        </View>
      )}

      {error && <Text style={styles.error}>{error}</Text>}

      {messages && (
        <FlatList
          ref={listRef}
          data={messages}
          keyExtractor={(m) => m.id}
          contentContainerStyle={styles.listContent}
          renderItem={({ item }) => {
            const parent = item.replyToId ? (parentLookup.get(item.replyToId) ?? null) : null;
            return (
              <MessageBubble
                message={item}
                mine={item.senderId === user?.id}
                senderName={senderName(item.senderId)}
                parent={parent}
                parentSenderName={parent ? senderName(parent.senderId) : ''}
                time={new Date(item.createdAt).toLocaleTimeString('de-DE', {
                  hour: '2-digit',
                  minute: '2-digit',
                })}
                reactions={reactions.get(item.id) ?? []}
                myUserId={user?.id ?? null}
                ownDeviceId={device?.id ?? null}
                ownPrivateKey={ownPrivateKey}
                onLongPress={() => setActiveMessage(item)}
                onToggleReaction={(emoji) => {
                  void handleReact(emoji, item.id);
                }}
              />
            );
          }}
          onContentSizeChange={() => listRef.current?.scrollToEnd({ animated: false })}
        />
      )}

      {replyTo && (
        <View style={styles.replyBanner}>
          <View style={styles.replyBannerLeft}>
            <Text style={styles.replyBannerLabel}>
              Antwort an {senderName(replyTo.senderId)}
            </Text>
            <Text style={styles.replyBannerBody} numberOfLines={1}>
              {replyTo.plaintext ?? '…'}
            </Text>
          </View>
          <Pressable onPress={() => setReplyTo(null)} hitSlop={10}>
            <Text style={styles.replyBannerClose}>×</Text>
          </Pressable>
        </View>
      )}

      <View style={styles.inputRow}>
        <Pressable
          style={styles.plusBtn}
          onPress={openImageMenu}
          disabled={sending}
          hitSlop={6}
        >
          <Text style={styles.plusText}>＋</Text>
        </Pressable>
        <TextInput
          value={text}
          onChangeText={setText}
          placeholder="Nachricht schreiben…"
          placeholderTextColor={colors.textDim}
          style={styles.input}
          multiline
          editable={!sending}
        />
        <Pressable
          style={[styles.sendBtn, (!text.trim() || sending) && styles.sendBtnDisabled]}
          disabled={!text.trim() || sending}
          onPress={handleSendText}
        >
          {sending ? (
            <ActivityIndicator color={colors.text} />
          ) : (
            <Text style={styles.sendBtnText}>Senden</Text>
          )}
        </Pressable>
      </View>

      <MessageActionsSheet
        visible={activeMessage !== null}
        mine={activeMessage?.senderId === user?.id}
        onClose={() => setActiveMessage(null)}
        onReact={(emoji) => {
          if (activeMessage) void handleReact(emoji, activeMessage.id);
        }}
        onReply={() => {
          if (activeMessage) setReplyTo(activeMessage);
        }}
        onDelete={() => {
          if (activeMessage) handleDelete(activeMessage.id);
        }}
      />
    </KeyboardAvoidingView>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: colors.bg },
  loading: { flex: 1, alignItems: 'center', justifyContent: 'center' },
  error: { color: colors.danger, padding: 12, textAlign: 'center' },
  listContent: { padding: 12 },
  replyBanner: {
    flexDirection: 'row',
    alignItems: 'center',
    paddingHorizontal: 12,
    paddingVertical: 8,
    borderTopWidth: 1,
    borderTopColor: colors.border,
    backgroundColor: colors.surface,
    gap: 12,
  },
  replyBannerLeft: { flex: 1 },
  replyBannerLabel: { color: colors.accent, fontSize: 12, fontWeight: '700' },
  replyBannerBody: { color: colors.textMuted, fontSize: 13, marginTop: 2 },
  replyBannerClose: { color: colors.textMuted, fontSize: 22, paddingHorizontal: 6 },
  inputRow: {
    flexDirection: 'row',
    alignItems: 'flex-end',
    padding: 8,
    gap: 8,
    borderTopWidth: 1,
    borderTopColor: colors.border,
    backgroundColor: colors.surface,
  },
  plusBtn: {
    width: 40,
    height: 40,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: colors.bg,
    borderRadius: 10,
    borderColor: colors.border,
    borderWidth: 1,
  },
  plusText: { color: colors.text, fontSize: 20, lineHeight: 22 },
  input: {
    flex: 1,
    minHeight: 40,
    maxHeight: 120,
    color: colors.text,
    backgroundColor: colors.bg,
    borderColor: colors.border,
    borderWidth: 1,
    borderRadius: 10,
    paddingHorizontal: 12,
    paddingVertical: 10,
    fontSize: 15,
  },
  sendBtn: {
    backgroundColor: colors.accent,
    borderRadius: 10,
    paddingHorizontal: 16,
    height: 40,
    alignItems: 'center',
    justifyContent: 'center',
  },
  sendBtnDisabled: { opacity: 0.5 },
  sendBtnText: { color: colors.text, fontWeight: '600' },
  callBtn: {
    paddingHorizontal: 10,
    paddingVertical: 4,
  },
  callBtnText: { fontSize: 18 },
});
