import React, {useCallback, useEffect, useState} from 'react';
import {
  Alert,
  LayoutChangeEvent,
  NativeScrollEvent,
  NativeSyntheticEvent,
  RefreshControl,
  StyleSheet,
  Text,
  View,
} from 'react-native';
import Animated from 'react-native-reanimated';
import {
  CategorySelectionScreenType,
  MomentCategoryType,
  MomentType,
  ProfilePreviewType,
  ProfileType,
  ScreenType,
  UserType,
} from '../../types';
import {
  COVER_HEIGHT,
  MOMENT_CATEGORIES,
  TAGG_TEXT_LIGHT_BLUE,
} from '../../constants';
import {fetchUserX, SCREEN_HEIGHT, userLogin} from '../../utils';
import TaggsBar from '../taggs/TaggsBar';
import {Moment} from '../moments';
import ProfileBody from './ProfileBody';
import ProfileCutout from './ProfileCutout';
import ProfileHeader from './ProfileHeader';
import {useDispatch, useSelector, useStore} from 'react-redux';
import {RootState} from '../../store/rootreducer';
import {
  friendUnfriendUser,
  blockUnblockUser,
  loadFriendsData,
  updateUserXFriends,
  updateMomentCategories,
  deleteUserMomentsForCategory,
} from '../../store/actions';
import {
  NO_USER,
  NO_PROFILE,
  EMPTY_PROFILE_PREVIEW_LIST,
  EMPTY_MOMENTS_LIST,
  MOMENT_CATEGORIES_MAP,
} from '../../store/initialStates';
import {Cover} from '.';
import {TouchableOpacity} from 'react-native-gesture-handler';
import {useNavigation} from '@react-navigation/native';
import GreyPlusLogo from '../../assets/icons/grey-plus-logo.svg';

interface ContentProps {
  y: Animated.Value<number>;
  userXId: string | undefined;
  screenType: ScreenType;
}

const Content: React.FC<ContentProps> = ({y, userXId, screenType}) => {
  const dispatch = useDispatch();

  const {user = NO_USER, profile = NO_PROFILE} = userXId
    ? useSelector((state: RootState) => state.userX[screenType][userXId])
    : useSelector((state: RootState) => state.user);

  const {friends = EMPTY_PROFILE_PREVIEW_LIST} = userXId
    ? useSelector((state: RootState) => state.userX[screenType][userXId])
    : useSelector((state: RootState) => state.friends);

  const {
    friends: friendsLoggedInUser = EMPTY_PROFILE_PREVIEW_LIST,
  } = useSelector((state: RootState) => state.friends);

  const {moments = EMPTY_MOMENTS_LIST} = userXId
    ? useSelector((state: RootState) => state.userX[screenType][userXId])
    : useSelector((state: RootState) => state.moments);

  const {momentCategories = MOMENT_CATEGORIES_MAP} = userXId
    ? useSelector((state: RootState) => state.userX[screenType][userXId])
    : useSelector((state: RootState) => state.momentCategories);

  const {blockedUsers = EMPTY_PROFILE_PREVIEW_LIST} = useSelector(
    (state: RootState) => state.blocked,
  );
  const {user: loggedInUser = NO_USER} = useSelector(
    (state: RootState) => state.user,
  );
  const state = useStore().getState();

  const navigation = useNavigation();

  /**
   * States
   */
  const [imagesMap, setImagesMap] = useState<Map<string, MomentType[]>>(
    new Map(),
  );
  const [isFriend, setIsFriend] = useState<boolean>(false);
  const [isBlocked, setIsBlocked] = useState<boolean>(false);
  const [profileBodyHeight, setProfileBodyHeight] = useState(0);
  const [shouldBounce, setShouldBounce] = useState<boolean>(true);
  const [refreshing, setRefreshing] = useState<boolean>(false);

  /**
   * Filter list of categories already selected by user
   */
  const userMomentCategories = MOMENT_CATEGORIES.filter(
    (category) => momentCategories[category] === true,
  );

  const onRefresh = useCallback(() => {
    const refrestState = async () => {
      if (!userXId) {
        await userLogin(dispatch, loggedInUser);
      } else {
        await fetchUserX(dispatch, user, screenType);
      }
    };
    setRefreshing(true);
    refrestState().then(() => {
      setRefreshing(false);
    });
  }, []);

  const onLayout = (e: LayoutChangeEvent) => {
    const {height} = e.nativeEvent.layout;
    setProfileBodyHeight(height);
  };

  const createImagesMap = useCallback(() => {
    var map = new Map();
    moments.forEach(function (imageObject) {
      var moment_category = imageObject.moment_category;
      if (map.has(moment_category)) {
        map.get(moment_category).push(imageObject);
      } else {
        map.set(moment_category, [imageObject]);
      }
    });
    setImagesMap(map);
  }, [moments]);

  useEffect(() => {
    createImagesMap();
  }, [createImagesMap]);

  /**
   * This hook is called on load of profile and when you update the friends list.
   */
  useEffect(() => {
    const isActuallyAFriend = friendsLoggedInUser.some(
      (friend) => friend.username === user.username,
    );
    if (isFriend != isActuallyAFriend) {
      setIsFriend(isActuallyAFriend);
    }
  }, [friendsLoggedInUser]);

  useEffect(() => {
    const isActuallyBlocked = blockedUsers.some(
      (cur_user) => user.username === cur_user.username,
    );
    if (isBlocked != isActuallyBlocked) {
      setIsBlocked(isActuallyBlocked);
    }
  }, [blockedUsers, user]);

  /**
   * The object returned by this method is added to the list of blocked / friended users by the reducer.
   * Which helps us prevent an extra api call to the backend just to fetch a user.
   */
  const getUserAsProfilePreviewType = (
    passedInUser: UserType,
    passedInProfile: ProfileType,
  ): ProfilePreviewType => {
    const fullName = passedInProfile.name.split(' ');
    return {
      id: passedInUser.userId,
      username: passedInUser.username,
      first_name: fullName[0],
      last_name: fullName[1],
    };
  };

  /**
   * Handles a click on the friend / unfriend button.
   * friendUnfriendUser takes care of updating the friends list for loggedInUser
   * updateUserXFriends updates friends list for the new friend.
   */

  const handleFriendUnfriend = async () => {
    await dispatch(
      friendUnfriendUser(
        loggedInUser,
        getUserAsProfilePreviewType(user, profile),
        isFriend,
      ),
    );
    await dispatch(updateUserXFriends(user.userId, state));
  };

  /**
   * Handles a click on the block / unblock button.
   * loadFriends updates friends list for the logged in user
   * updateUserXFriends updates friends list for the user.
   */
  const handleBlockUnblock = async (callback?: () => void) => {
    await dispatch(
      blockUnblockUser(
        loggedInUser,
        getUserAsProfilePreviewType(user, profile),
        isBlocked,
      ),
    );
    await dispatch(loadFriendsData(loggedInUser.userId));
    await dispatch(updateUserXFriends(user.userId, state));
    if (callback) {
      callback();
    }
  };

  /**
   * Handle deletion of a category
   * Confirm with user before deleting the category
   * @param category category to be deleted
   */
  const handleCategoryDeletion = (category: MomentCategoryType) => {
    Alert.alert(
      'Category Deletion',
      `Are you sure that you want to delete the category ${category} ?`,
      [
        {
          text: 'Cancel',
          style: 'cancel',
        },
        {
          text: 'Yes',
          onPress: () => {
            dispatch(
              updateMomentCategories([category], false, loggedInUser.userId),
            );
            dispatch(deleteUserMomentsForCategory(category));
          },
        },
      ],
      {cancelable: true},
    );
  };

  const handleScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
    /**
     * Set the new y position
     */
    const newY = e.nativeEvent.contentOffset.y;
    y.setValue(newY);

    /**
     * Do not allow overflow of scroll on bottom of the screen
     * SCREEN_HEIGHT - COVER_HEIGHT = Height of the scroll view
     */
    if (newY >= SCREEN_HEIGHT - COVER_HEIGHT) {
      setShouldBounce(false);
    } else if (newY === 0) {
      setShouldBounce(true);
    }
  };

  return (
    <Animated.ScrollView
      style={styles.container}
      onScroll={(e) => handleScroll(e)}
      bounces={shouldBounce}
      showsVerticalScrollIndicator={false}
      scrollEventThrottle={1}
      stickyHeaderIndices={[4]}
      refreshControl={
        <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
      }>
      <Cover {...{userXId, screenType}} />
      <ProfileCutout />
      <ProfileHeader
        {...{userXId, screenType, handleBlockUnblock, isBlocked}}
      />
      <ProfileBody
        {...{
          onLayout,
          userXId,
          screenType,
          isFriend,
          handleFriendUnfriend,
          handleBlockUnblock,
          isBlocked,
        }}
      />
      <TaggsBar {...{y, profileBodyHeight, userXId, screenType}} />
      <View style={styles.momentsContainer}>
        {userXId && moments.length === 0 && (
          <View style={styles.plusIconContainer}>
            <GreyPlusLogo width={90} height={90} />
            <Text style={styles.noMomentsText}>{`Looks like ${
              profile.name.split(' ')[0]
            } has not posted any moments yet`}</Text>
          </View>
        )}
        {userMomentCategories.map(
          (title, index) =>
            (!userXId || imagesMap.get(title)) && (
              <Moment
                key={index}
                title={title}
                images={imagesMap.get(title)}
                userXId={userXId}
                screenType={screenType}
                handleMomentCategoryDelete={handleCategoryDeletion}
                shouldAllowDeletion={userMomentCategories.length > 2}
              />
            ),
        )}
        {!userXId && userMomentCategories.length < 6 && (
          <TouchableOpacity
            onPress={() =>
              navigation.push('CategorySelection', {
                categories: momentCategories,
                screenType: CategorySelectionScreenType.Profile,
                user: loggedInUser,
              })
            }
            style={styles.createCategoryButton}>
            <Text style={styles.createCategoryButtonLabel}>
              Create a new category
            </Text>
          </TouchableOpacity>
        )}
      </View>
    </Animated.ScrollView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  momentsContainer: {
    backgroundColor: '#f2f2f2',
    paddingBottom: SCREEN_HEIGHT / 10,
    flex: 1,
    flexDirection: 'column',
  },
  createCategoryButton: {
    backgroundColor: TAGG_TEXT_LIGHT_BLUE,
    justifyContent: 'center',
    alignItems: 'center',
    width: '70%',
    height: 30,
    marginTop: '15%',
    alignSelf: 'center',
  },
  createCategoryButtonLabel: {
    fontSize: 16,
    fontWeight: '500',
    color: 'white',
  },
  plusIconContainer: {
    flexDirection: 'column',
    justifyContent: 'center',
    alignItems: 'center',
    marginVertical: '10%',
  },
  noMomentsText: {
    fontSize: 14,
    fontWeight: 'bold',
    color: 'gray',
    marginVertical: '8%',
  },
});

export default Content;
