import React, { useState, useRef, useEffect } from "react";
import { Animated, Easing, View, Text, TouchableOpacity, Image, Alert } from "react-native";
import { styles } from "./addNoteStyle";
import { useDispatch, useSelector } from "react-redux";
import {setTitle,setNote,setImageUrl,setAudioUrl,resetAddNoteState} from "../../store/addNoteSlice";
import { selectAddNote } from '../../store/addNoteSlice';
import { collection, addDoc, setDoc, doc, serverTimestamp } from "firebase/firestore";
import { firebaseDb } from "../../../firebaseConfig";
import { getStorage, ref, uploadString, getDownloadURL } from "firebase/storage";
import { Entypo, Feather, AntDesign } from "@expo/vector-icons";
import { RichEditor, RichToolbar, actions } from "react-native-pell-rich-editor";
import * as ImagePicker from 'expo-image-picker';
import { Audio } from "expo-av";
import { useFocusEffect } from "@react-navigation/native";

const AddNote = ({ navigation, route }) => {
  const titleEditorRef = useRef(null);
  const noteEditorRef = useRef(null);

  const { title, note, imageUrl, audioUrl } = useSelector(selectAddNote);
  const dispatch = useDispatch();
  const [recording, setRecording] = useState();
  const [isRecording, setIsRecording] = useState(false);
  const [isPlaying, setIsPlaying] = useState(false);
  const [isEditing, setIsEditing] = useState(false);
  const [recordedAudios, setRecordedAudios] = useState([]);
  const [currentlyPlayingIndex, setCurrentlyPlayingIndex] = useState(null);
  const [soundObject, setSoundObject] = useState(null);
  
  const [opacityValue] = useState(new Animated.Value(1));

  const startRecordingAnimation = () => {
    Animated.loop(
      Animated.timing(
        opacityValue,
        {
          toValue: 0,
          duration: 800,
          easing: Easing.linear,
          useNativeDriver: true
        }
      )
    ).start();
  };
  
  const stopRecordingAnimation = () => {
    Animated.timing(
      opacityValue,
      {
        toValue: 1,
        duration: 800,
        easing: Easing.linear,
        useNativeDriver: true
      }
    ).start();
  };

  useEffect(() => {
    if (isRecording) {
      startRecordingAnimation();
    } else {
      stopRecordingAnimation();
    }
  }, [isRecording]);

  useEffect(() => {
    if (route.params && route.params.noteData) {
      const { title, note, image, audio } = route.params.noteData;
      dispatch(setTitle(title || ""));
      dispatch(setNote(note || ""));
      dispatch(setImageUrl(image || ""));
      dispatch(setAudioUrl(audio || ""));
      setIsEditing(true); 
    } else {
      dispatch(resetAddNoteState());
    }
  }, [route.params]);
  
  useEffect(() => {
    return () => {
      dispatch(resetAddNoteState());
    };
  }, []);
  
  // useFocusEffect(
  //   React.useCallback(() => {
  //     const clearEditorContent = () => {
  //       titleEditorRef.current?.setContentHTML('');
  //       noteEditorRef.current?.setContentHTML('');
  //     };

  //     clearEditorContent();

  //     return () => {

  //     };
  //   }, [])
  // );
  

  const handleTitleChange = (text) => {
    dispatch(setTitle(text)); 
  };
  
  const handleNoteChange = (text) => {
    dispatch(setNote(text)); 
  };

  const handleImagePick = async () => {
    try {
      const permissionResult = await ImagePicker.requestMediaLibraryPermissionsAsync();
      if (!permissionResult.granted) {
        Alert.alert("Permission to access camera roll is required!");
        return;
      }
  
      const pickerResult = await ImagePicker.launchImageLibraryAsync({
        mediaTypes: ImagePicker.MediaTypeOptions.Images,
        allowsEditing: true,
        aspect: [4, 3],
        quality: 1,
      });
  
      if (!pickerResult.canceled) {
        const { uri } = pickerResult.assets?.[0] || {};
        if (uri) dispatch(setImageUrl(uri));
      }
    } catch (error) {
      console.error("Error picking an image:", error);
    }
  };
  
  const uploadImageToStorage = async () => {
    try {
      const storage = getStorage();
      const storageRef = ref(storage, `images/${new Date().toISOString()}`);
      const response = await fetch(imageUrl);
      const blob = await response.blob();
      const snapshot = await uploadString(storageRef, blob);
      return await getDownloadURL(snapshot.ref);
    } catch (error) {
      console.error("Error uploading image to storage:", error);
    }
  };

  const uploadAudioToStorage = async () => {
    try {
      const storage = getStorage();
      const audioStorageRef = ref(storage, `audio/${new Date().toISOString()}.m4a`);
      const response = await fetch(audioUrl);
      const blob = await response.blob();
      const snapshot = await uploadString(audioStorageRef, blob);
      return await getDownloadURL(snapshot.ref);
    } catch (error) {
      console.error("Error uploading audio to storage:", error);
      throw error;
    }
  };

  const handleToggleRecording = async () => {
    try {
      const { status } = await Audio.requestPermissionsAsync();
      if (status !== "granted") {
        Alert.alert("Permission to record audio is required!");
        return;
      }

      if (!isRecording) {
        const recording = new Audio.Recording();
        await recording.prepareToRecordAsync(Audio.RECORDING_OPTIONS_PRESET_HIGH_QUALITY);
        await recording.startAsync();
        setRecording(recording);
        setIsRecording(true);
      } else {
        await recording.stopAndUnloadAsync();
        const uri = recording.getURI();
        setRecordedAudios([...recordedAudios, uri]);
        setIsRecording(false);
      }
    } catch (error) {
      console.error("Error toggling audio recording:", error);
    }
  };

  const handleAudioPlay = async (index) => {
    try {
      if (soundObject && currentlyPlayingIndex !== null && currentlyPlayingIndex === index) {
        const status = await soundObject.getStatusAsync();
        if (status.isPlaying) {
          await soundObject.pauseAsync();
          setIsPlaying(false);
        } else {
          await soundObject.playAsync();
          setIsPlaying(true);
        }
      } else {
        if (soundObject) {
          await soundObject.unloadAsync();
        }
        const newSoundObject = new Audio.Sound();
        await newSoundObject.loadAsync({ uri: recordedAudios[index] });
        await newSoundObject.playAsync();
        setIsPlaying(true);
        setCurrentlyPlayingIndex(index);
        setSoundObject(newSoundObject);
      }
    } catch (error) {
      console.error("Error playing audio:", error);
    }
  };

  const handleSave = async () => {
    try {
      if (imageUrl) {
        dispatch(setImageUrl(await uploadImageToStorage()));
      }
      if (audioUrl) {
        dispatch(setAudioUrl(await uploadAudioToStorage()))
      }
  
      const uploadedAudioUrls = [];
      for (const recordedAudio of recordedAudios) {
        const audioStorageRef = ref(
          getStorage(),
          `recorded_audios/${new Date().toISOString()}.m4a`
        );
        const response = await fetch(recordedAudio);
        const blob = await response.blob();
        const snapshot = await uploadString(audioStorageRef, blob);
        const downloadUrl = await getDownloadURL(snapshot.ref);
        uploadedAudioUrls.push(downloadUrl);
      }

      const noteObject = {
        title, note, image: imageUrl, audios: uploadedAudioUrls, created_at: serverTimestamp()
      };
  
      if (isEditing) {
        const { id } = route.params.noteData;
        await setDoc(doc(firebaseDb, "notes", id), noteObject);
      } else {
        await addDoc(collection(firebaseDb, "notes"), noteObject);
      }
  
      dispatch(resetAddNoteState());
      navigation.navigate("Home");
    } catch (error) {
      console.error("Error saving note:", error);
    }
  };
  

  return (
    <View style={styles.container}>
      <View style={styles.headerIcon}>
        <View style={styles.headerIconLeft}>
          <TouchableOpacity onPress={() => { navigation.navigate("Home"); }}>
            <Feather name="arrow-left" size={24} color="black" />
          </TouchableOpacity>
        </View>
        <View style={styles.headerIconRight}>
          <TouchableOpacity onPress={handleToggleRecording} style={styles.microphoneButton}>
            <Animated.View style={{ opacity: opacityValue }}>
              <Entypo name={isRecording ? "controller-stop" : "mic"} size={24} color="black" />
            </Animated.View>
          </TouchableOpacity>
          <TouchableOpacity onPress={handleImagePick}>
            <Entypo name="folder-images" size={24} color="black" />
          </TouchableOpacity>
        </View>
      </View>

      <View style={{ alignItems: "center", justifyContent: "center" }}>
        {imageUrl && <Image source={{ uri: imageUrl }} style={styles.inputImg} />}
      </View>

      <RichEditor
        ref={titleEditorRef}
        style={[styles.inputTtl, { backgroundColor: "#fff" }]}
        onChange={handleTitleChange}
        initialContentHTML={title}
        placeholder="Enter title"
      />
      <RichEditor
        ref={noteEditorRef}
        style={[styles.inputNote, { backgroundColor: "#fff" }]}
        onChange={handleNoteChange}
        initialContentHTML={note}
        placeholder="Enter note"
      />

      {recordedAudios.map((audioUrl, index) => (
        <View key={index} style={styles.audioPlay}>
          <Text style={styles.audioButtonText}>Play Audio {index + 1}</Text>
          <TouchableOpacity onPress={() => handleAudioPlay(index)}>
            <AntDesign name={(isPlaying && currentlyPlayingIndex === index) ? "pausecircle" : "play"} size={20} color="black" />
          </TouchableOpacity>
        </View>
      ))}

      <RichToolbar
        getEditor={() => noteEditorRef.current}
        actions={[
          actions.setBold,
          actions.setItalic,
          actions.setUnderline,
          actions.insertBulletsList,
          actions.insertOrderedList,
          actions.checkboxList,
          actions.undo,
          actions.redo,
          actions.insertLink,
        ]}
        style={styles.toolbar}
      />
      <View style={{ alignItems: "center" }}>
      <TouchableOpacity style={styles.addBtn} onPress={handleSave}>
      <Text style={styles.addBtnText}>{isEditing ? "Save" : "Add"}</Text>
    </TouchableOpacity>
      </View>
    </View>
  );
};

export default AddNote;

import React, { useState, useEffect } from "react";
import { Text, View, FlatList, TouchableOpacity, Image, Alert, useWindowDimensions } from "react-native";
import { collection, deleteDoc, doc, onSnapshot  } from "firebase/firestore";
import { firebaseDb } from "../../firebaseConfig";
import { AntDesign, Feather } from "@expo/vector-icons";
import { Audio } from "expo-av";
import HTML from 'react-native-render-html';
import { colors } from "../util/color";

const NoteList = ({ navigation }) => {
  const [noteList, setNoteList] = useState([]);
  const [expandedNoteId, setExpandedNoteId] = useState(null);

  useEffect(() => {
    const getNoteList = onSnapshot(collection(firebaseDb, "notes"), (snapshot) => {
      setNoteList(
        snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), isPlaying: false }))
      );
    });
    return () => getNoteList();
  }, []);
  
  const truncateText = (text, length) => {
    return text.length > length ? text.substring(0, length) + "..." : text;
  };

  const deleteNote = async (id) => {
    try {
      await deleteDoc(doc(firebaseDb, "notes", id));
      setNoteList((prevNotes) => prevNotes.filter((note) => note.id !== id));
    } catch (error) {
      console.error("Error deleting note:", error);
    }
  };

  const handleDeletePress = (id) => {
    Alert.alert(
      "Delete Note",
      "Are you sure you want to delete this note?",
      [
        { text: "Cancel", style: "cancel" },
        {
          text: "Delete",
          style: "destructive",
          onPress: () => deleteNote(id),
        },
      ],
      { cancelable: false }
    );
  };

  const handleNoteItemPress = (id) => {
    if (expandedNoteId === id) {
      navigation.navigate('AddNote', { noteData: noteList.find(note => note.id === id) });
    } else {
      setExpandedNoteId(id);
    }
  };

  const handlePlayAudio = async (audioUrl, index) => { 
    try {
      const soundObject = new Audio.Sound();
      await soundObject.loadAsync({ uri: audioUrl });
      await soundObject.playAsync();
      setNoteList((prevNotes) =>
        prevNotes.map((note, i) =>
          i === index ? { ...note, isPlaying: true } : note
        )
      );
      soundObject.setOnPlaybackStatusUpdate((status) => {
        if (status.didJustFinish) {
          setNoteList((prevNotes) =>
            prevNotes.map((note, i) =>
              i === index ? { ...note, isPlaying: false } : note
            )
          );
        }
      });
    } catch (error) {
      console.error("Error playing audio:", error);
      Alert.alert("Error", "Could not play audio.");
    }
  };

  const windowWidth = useWindowDimensions().width;

  return (
    <View style={styles.container}>
      <FlatList
        data={noteList}
        renderItem={({ item, index }) => (
          <TouchableOpacity  onPress={() => handleNoteItemPress(item.id)}>
            <View style={[styles.noteItem, {height: expandedNoteId === item.id ? null : 110}]}>
              <View style={styles.noteInfo}>
                <HTML
                  source={{
                    html: `<h3 style="font-weight: bold;">${item.title}</h3>`,
                  }}
                  contentWidth={windowWidth}
                  ignoredDomTags={['input']}
                />
                {expandedNoteId === item.id ? (
                  <HTML
                    source={{ html: item.note }}
                    contentWidth={windowWidth}
                    ignoredDomTags={['input']}
                  />
                ) : (
                  <HTML
                    source={{ html: truncateText(item.note, 25) }}
                    contentWidth={windowWidth}
                    ignoredDomTags={['input']}
                  />
                )}
                {expandedNoteId === item.id && item.image && (
                  <Image
                    source={{ uri: item.image }}
                    style={{ width: "80%", height: 150, borderRadius: 10, marginVertical: 10 }}
                  />
                )}
                {expandedNoteId === item.id && item.audios && (
                  <TouchableOpacity onPress={() => handlePlayAudio(item.audios, index)} style={styles.playAudioButton}>
                    <AntDesign name={item.isPlaying ? "pausecircle" : "play"} size={20} color="black" />
                  </TouchableOpacity>

                  {recordedAudios.map((audioUrl, index) => (
                    <View key={index} style={styles.audioPlay}>

                      <TouchableOpacity onPress={() => handleAudioPlay(index)}>
                        <AntDesign name={(isPlaying && currentlyPlayingIndex === index) ? "pausecircle" : "play"} size={20} color="black" />
                      </TouchableOpacity>
                    </View>
                  ))}
                )}

                <Text style={styles.createdAt}>
                  {item.created_at && new Date(item.created_at?.toDate()).toLocaleString([], { hour: 'numeric', minute: 'numeric', hour12: true })}
                </Text>
          
              </View>

              {expandedNoteId === item.id && (
                <TouchableOpacity onPress={() => setExpandedNoteId(null)} style={styles.minimizeButton}>
                  <Feather name="minimize-2" size={20} color="black" />
                </TouchableOpacity>
              )}
        
              <TouchableOpacity
                onPress={() => handleDeletePress(item.id)}
                style={styles.deleteButton}
              >
                <AntDesign name="delete" size={20} color="black" />
              </TouchableOpacity>

            </View>
          </TouchableOpacity>
        )}
        keyExtractor={(item) => item.id}
        contentContainerStyle={styles.listContainer}
        showsVerticalScrollIndicator={false}
      />
    </View>
  );
};

const styles = {
  container: {
    flex: 1,
    backgroundColor: colors.primary,
  },
  listContainer: {
    paddingHorizontal: 10,
  },
  noteItem: {
    marginBottom: 10,
    paddingHorizontal: 20,
    borderRadius: 20,
    backgroundColor: "#FEFEFA",
    flexDirection: "row",
    justifyContent: "space-between",
    alignItems: "center",
    paddingVertical: 10,
  },
  noteInfo: {
    flex: 1,
  },
  title: {
    fontWeight: 'bold',
    marginBottom: 8,
  },
  noteContent: {
    marginBottom: 8,
  },
  image: {
    width: 100,
    height: 100,
    marginVertical: 10,
  },
  createdAt: {
    fontSize: 12, 
    color: 'gray', 
    marginTop: 4,
  },
  minimizeButton: {
    position: 'absolute',
    top: 10,
    right: 10,
  },
  deleteButton: {
    padding: 10,
  },
  playAudioButton: {
    padding: 10,
  },
};

export default NoteList;

item.audios error