import React, { useCallback, useEffect, useRef, useState } from 'react';
import { StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native';

import { SafeAreaView } from 'react-native-safe-area-context';

import { useFocusEffect } from '@react-navigation/native';
import type { RouteProp } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import type { Channel as StreamChatChannel } from 'stream-chat';
import {
  Channel,
  MessageComposer,
  MessageList,
  UserAdd,
  WithComponents,
  useTheme,
} from 'stream-chat-react-native';

import { NewDirectMessagingSendButton } from '../components/NewDirectMessagingSendButton';
import { RoundButton } from '../components/RoundButton';
import { ScreenHeader } from '../components/ScreenHeader';
import { SelectedUserTag } from '../components/UserSearch/SelectedUserTag';
import { UserSearchResults } from '../components/UserSearch/UserSearchResults';
import { useAppContext } from '../context/AppContext';
import { useUserSearchContext } from '../context/UserSearchContext';
import { Group } from '../icons/Group';
import { User } from '../icons/User';
import { useLegacyColors } from '../theme/useLegacyColors';

import type { StackNavigatorParamList } from '../types';

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  createGroupButtonContainer: {
    alignItems: 'center',
    flexDirection: 'row',
    paddingHorizontal: 8,
    paddingVertical: 16,
  },
  createGroupButtonText: {
    fontSize: 14,
    fontWeight: '700',
    paddingLeft: 8,
  },
  emptyMessageContainer: {
    alignItems: 'center',
    flex: 1,
    justifyContent: 'center',
  },
  inputBox: {
    flex: 1,
    fontSize: 14,
    includeFontPadding: false, // for android vertical text centering
    padding: 0, // removal of default text input padding on android
    paddingRight: 16,
    paddingTop: 0, // removal of iOS top padding for weird centering
    textAlignVertical: 'center', // for android vertical text centering
  },
  inputBoxContainer: {
    flexDirection: 'row',
  },
  noChats: { fontSize: 12 },
  searchContainer: {
    borderBottomWidth: 1,
    flexDirection: 'row',
  },
  searchContainerLeft: {
    fontSize: 12,
    paddingHorizontal: 16,
    paddingVertical: 20,
    textAlignVertical: 'center',
  },
  searchContainerMiddle: {
    flex: 1,
    justifyContent: 'center',
  },
  searchContainerRight: {
    justifyContent: 'flex-end',
    paddingBottom: 16,
    paddingRight: 16,
  },
  selectedUsersContainer: {
    flexDirection: 'row',
    flexWrap: 'wrap',
  },
});

export const EmptyMessagesIndicator = () => {
  const { grey } = useLegacyColors();
  return (
    <View style={styles.emptyMessageContainer}>
      <Text
        style={[
          styles.noChats,
          {
            color: grey,
          },
        ]}
      >
        No chats here yet...
      </Text>
    </View>
  );
};

export type NewDirectMessagingScreenNavigationProp = NativeStackNavigationProp<
  StackNavigatorParamList,
  'NewDirectMessagingScreen'
>;

export type NewDirectMessagingScreenProps = {
  navigation: NewDirectMessagingScreenNavigationProp;
  route: RouteProp<StackNavigatorParamList, 'NewDirectMessagingScreen'>;
};

export const NewDirectMessagingScreen: React.FC<NewDirectMessagingScreenProps> = ({
  navigation,
  route,
}) => {
  const {
    theme: { semantics },
  } = useTheme();
  const { accent_blue, black, grey, white } = useLegacyColors();
  const { chatClient } = useAppContext();

  const {
    onChangeSearchText,
    onFocusInput,
    reset,
    results,
    searchText,
    selectedUserIds,
    selectedUsers,
    toggleUser,
  } = useUserSearchContext();

  const messageInputRef = useRef<TextInput | null>(null);
  const searchInputRef = useRef<TextInput>(null);
  const currentChannel = useRef<StreamChatChannel>(undefined);
  const isDraft = useRef(true);
  const initialUserIdRef = useRef<string>(undefined);

  const [focusOnMessageInput, setFocusOnMessageInput] = useState(false);
  const [focusOnSearchInput, setFocusOnSearchInput] = useState(true);

  useEffect(() => {
    const initialUser = route.params?.initialUser;
    if (!initialUser || initialUserIdRef.current === initialUser.id) {
      return;
    }
    // Ensures we initialize the selection only once per navigation.
    initialUserIdRef.current = initialUser.id;
    reset();
    toggleUser(initialUser);
  }, [route.params?.initialUser, reset, toggleUser]);

  // When selectedUsers are changed, initiate a channel with those users as members,
  // and set it as a channel on current screen.
  const selectedUsersLength = selectedUsers.length;

  useFocusEffect(
    React.useCallback(() => {
      if (selectedUsersLength === 0) {
        currentChannel.current = undefined;
        isDraft.current = true;
        setFocusOnMessageInput(false);
        setFocusOnSearchInput(true);
      }
    }, [selectedUsersLength]),
  );

  useEffect(() => {
    const initChannel = async () => {
      if (!chatClient?.user?.id) {
        return;
      }

      // If there are no selected users, then set dummy channel.
      if (selectedUsersLength === 0) {
        currentChannel.current = undefined;
        isDraft.current = true;
        setFocusOnMessageInput(false);
        setFocusOnSearchInput(true);
        return;
      }

      // With members selected, collapse the user search so the composer takes over.
      // The manual "tap a user in the list" path sets this directly; doing it here as
      // well covers the seeded path (navigated in with a preselected user).
      setFocusOnSearchInput(false);

      const members = [chatClient.user.id, ...selectedUserIds];

      // Check if the channel already exists.
      const channels = await chatClient.queryChannels({
        members,
      });

      if (channels.length === 1) {
        // Channel already exist
        currentChannel.current = channels[0];
        isDraft.current = false;
      } else {
        // Channel doesn't exist.
        isDraft.current = true;

        const channel = chatClient.channel('messaging', {
          members,
        });

        // Hack to trick channel component into accepting channel without watching it.
        channel.initialized = true;
        currentChannel.current = channel;
      }

      if (messageInputRef.current) {
        messageInputRef.current.focus();
      }
      setFocusOnMessageInput(true);
    };

    initChannel();
  }, [chatClient, selectedUserIds, selectedUsersLength]);

  const onBackPress = useCallback(() => {
    reset();

    if (!navigation.canGoBack()) {
      navigation.reset({ index: 0, routes: [{ name: 'MessagingScreen' }] });
      return;
    }

    navigation.goBack();
  }, [navigation, reset]);

  const renderUserSearch = ({ inSafeArea }: { inSafeArea: boolean }) => (
    <View style={[{ backgroundColor: white }, focusOnSearchInput ? styles.container : undefined]}>
      <ScreenHeader inSafeArea={inSafeArea} onBack={onBackPress} titleText='New Chat' />
      <TouchableOpacity
        activeOpacity={1}
        onPress={() => {
          setFocusOnMessageInput(false);
          setFocusOnSearchInput(true);
          if (searchInputRef.current) {
            searchInputRef.current.focus();
          }
        }}
        style={[
          styles.searchContainer,
          {
            backgroundColor: white,
            borderBottomColor: semantics.borderCoreDefault,
          },
        ]}
      >
        <Text
          style={[
            styles.searchContainerLeft,
            {
              color: grey,
            },
          ]}
        >
          TO:
        </Text>
        <View style={styles.searchContainerMiddle}>
          <View style={styles.selectedUsersContainer}>
            {selectedUsers.map((tag, index) => {
              const tagProps = {
                disabled: !focusOnSearchInput,
                index,
                onPress: () => {
                  toggleUser(tag);
                },
                tag,
              };

              return <SelectedUserTag key={index} {...tagProps} />;
            })}
          </View>
          {focusOnSearchInput && (
            <View style={styles.inputBoxContainer}>
              <TextInput
                onChangeText={onChangeSearchText}
                onFocus={onFocusInput}
                placeholder='Type a name'
                placeholderTextColor={grey}
                ref={searchInputRef}
                style={[
                  styles.inputBox,
                  {
                    color: black,
                    paddingBottom: selectedUsers.length ? 16 : 0,
                  },
                ]}
                value={searchText}
              />
            </View>
          )}
        </View>
        <View style={styles.searchContainerRight}>
          {selectedUsers.length === 0 ? (
            <User pathFill={grey} />
          ) : (
            <UserAdd pathFill={grey} height={20} width={20} />
          )}
        </View>
      </TouchableOpacity>
      {focusOnSearchInput && !searchText && selectedUsers.length === 0 && (
        <TouchableOpacity
          onPress={() => {
            navigation.push('NewGroupChannelAddMemberScreen');
          }}
          style={styles.createGroupButtonContainer}
        >
          <RoundButton>
            <Group pathFill={accent_blue} />
          </RoundButton>
          <Text
            style={[
              styles.createGroupButtonText,
              {
                color: black,
              },
            ]}
          >
            Create a Group
          </Text>
        </TouchableOpacity>
      )}
      {results && focusOnSearchInput && (
        <UserSearchResults
          toggleSelectedUser={(user) => {
            setFocusOnSearchInput(false);
            toggleUser(user);
          }}
        />
      )}
    </View>
  );

  if (!chatClient) {
    return null;
  }

  if (!currentChannel.current) {
    return renderUserSearch({ inSafeArea: false });
  }

  return (
    <SafeAreaView
      style={[
        styles.container,
        {
          backgroundColor: white,
        },
      ]}
    >
      <Channel
        additionalTextInputProps={{
          onFocus: () => {
            setFocusOnMessageInput(true);
            setFocusOnSearchInput(false);
            if (messageInputRef.current) {
              messageInputRef.current.focus();
            }
          },
        }}
        audioRecordingEnabled={true}
        channel={currentChannel.current}
        enforceUniqueReaction
        keyboardVerticalOffset={0}
        overrideOwnCapabilities={{ sendMessage: true }}
        setInputRef={(ref) => (messageInputRef.current = ref)}
      >
        {renderUserSearch({ inSafeArea: true })}
        {results && results.length >= 0 && !focusOnSearchInput && focusOnMessageInput && (
          <MessageList />
        )}
        <WithComponents overrides={{ SendButton: NewDirectMessagingSendButton }}>
          <MessageComposer />
        </WithComponents>
      </Channel>
    </SafeAreaView>
  );
};