// config
var website = "https://nullchat.alwaysdata.net"
var GroupListingDiv = "GroupListing"
var MessagesDiv = "Messages" // Changed from "Messages" to keep it on top
// Variables
var currentLatestMessageTime = new Date().toISOString(); // the default, is reset every time
var currentGroupID
var UserID
var UserCache = {}
var UserAuth
const MessageFormDiv = document.getElementById('MessageSender');
const MessageValuedDiv = document.getElementById('MessageToSend');
const UsersDiv = document.getElementById('Users');
const createGCDiv = document.getElementById("CreateGC")
const FriendsDiv = document.getElementById("Friends")
const currentFriendsDiv = document.getElementById("currentFriends")
const pendingFriendsDiv = document.getElementById("friendRequest")
const addUsersDiv = document.getElementById("UsersAdd")
const loginDiv = document.getElementById("Login")
const marker = document.getElementById("top-marker");

var reachedChatEnd = false
// Frontend


MessageFormDiv.addEventListener('submit', async function(event) {
  event.preventDefault();
  await sendMessages(currentGroupID, UserID, MessageValuedDiv.value)
  MessageValuedDiv.value = ""
});


const observer = new IntersectionObserver(async (entries) => {
    if (entries[0].isIntersecting) {
        await f_loadMessages(currentGroupID)
    }
});

observer.observe(marker);

async function f_createMessage(userId, message) {
  var userName = await getUserCache(userId)
  //alert(userName)
  document.getElementById(MessagesDiv).insertAdjacentHTML("beforeend", `<div class='Message'><br><b>${userName["Username"]}:</b><br>${message}<br><br></div>`)
}
async function f_login(newUsername,newPassword) {
const permission = await Notification.requestPermission();

    await login(newUsername,newPassword)
    await f_GroupButtons(UserID)
    loginDiv.style = "visibility: hidden;"
    await createSubscription()
}
async function f_GroupButtons(userID) {
    var UserGroups = await getUserGroups(userID)
    for (i in UserGroups){
        var groupInfo = await getGroupInfo(UserGroups[i])
        var icon = await getFile("Groups",UserGroups[i],groupInfo["Icon"])
        var iconSrc = URL.createObjectURL(icon);

        document.getElementById(GroupListingDiv).insertAdjacentHTML("beforeend", `
            <button onclick="f_LoadChat('${UserGroups[i]}')"><img src="${iconSrc}" style="width: 64px; height: 64px;"/>
            </button><br>
            `)
    }
}
async function f_CreateGroup() {
    document.getElementById("GC-Create-Name").value = ""
    document.getElementById("GC-Create-Icon").value = ""
    createGCDiv.style = "visibility: visible;"
}
async function f_ShowFriends() {
    document.getElementById("GC-Create-Name").value = ""
    document.getElementById("GC-Create-Icon").value = ""
    FriendsDiv.style = "visibility: visible;"
    //alert(await getFriends(UserID))
    f_loadFriends()
}

async function loadAddUsers() {
  document.getElementById("AddUser").style = "visibility: visible;"
  addUsersDiv.innerHTML = "";
  //alert(currentGroupID)
  var usersInGroup = await getGroupUsers(currentGroupID)
  //alert(usersInGroup)
  var friends = await getFriends(UserID)
  //alert("friends:" + friends)
  for (i in friends) {
    if (usersInGroup.includes(friends[i])){
      continue
    }
  getFriendRecordID(UserID, friends[i])
    var UserName = (await getUserCache(friends[i]))["Username"]
addUsersDiv.insertAdjacentHTML("beforeend", `${UserName}<button onclick="addToGC('${currentGroupID}', '${friends[i]}', 'Normal')">Add</button> <br>`)
  }
}

async function f_LoadChat(GroupID) {
    currentGroupID = GroupID
    //alert(GroupID)
    await f_loadGC(GroupID)
    document.getElementById(MessagesDiv).scrollTop = document.getElementById(MessagesDiv).scrollHeight;
      const container = document.getElementById("Messages");
  const oldHeight = container.scrollHeight;
  const oldScrollTop = container.scrollTop;
  const newHeight = container.scrollHeight;
  container.scrollTop = container.scrollHeight;
    await f_loadUsers()
    
}
async function f_loadUsers() {
  var GroupUsers = await getGroupUsers(currentGroupID)
  //alert("GU" + GroupUsers)
  for (i in GroupUsers){
    var userInfo = await getUserCache(GroupUsers[i])
    UsersDiv.insertAdjacentHTML("beforeend", `${userInfo["name"]} (${userInfo["Username"]}) <br>`)}
}

async function f_loadMessages(GroupID) {
  const container = document.getElementById("Messages");

  const oldHeight = container.scrollHeight;
  const oldScrollTop = container.scrollTop;
    var Messages = (await getMessages(GroupID, currentLatestMessageTime))
    //alert(Messages)
    for (i in Messages){
    await f_createMessage((Messages[i])["Creator"], (Messages[i])["Message"])
    }
    const newHeight = container.scrollHeight;
    container.scrollTop = oldScrollTop + (newHeight - oldHeight);
}

async function f_loadGC(GroupChat) {
document.querySelectorAll('.Message').forEach(el => el.remove());
    currentLatestMessageTime = new Date().toISOString();
  await f_loadMessages(GroupChat)
}
async function f_loadFriends() {
  pendingFriendsDiv.innerHTML = "";
  var pendingFriends =  await getPendingFriendRequests(UserID)
  for (i in pendingFriends){
  var UserName = (await getUserCache(pendingFriends[i]))["Username"]
pendingFriendsDiv.insertAdjacentHTML("beforeend", `${UserName} <button onclick="acceptFriend('${UserID}', '${pendingFriends[i]}')">Accept</button> <button onclick="removeFriend('${UserID}', '${pendingFriends[i]}')">Deny</button>`)
  }
  currentFriendsDiv.innerHTML = "";
  var currentFriends =  await getFriends(UserID)
  for (i in currentFriends){
  getFriendRecordID(UserID, currentFriends[i])
    var UserName = (await getUserCache(currentFriends[i]))["Username"]
currentFriendsDiv.insertAdjacentHTML("beforeend", `${UserName}<button onclick="removeFriend('${UserID}', '${currentFriends[i]}')">Remove</button> <br>`)
  }
}



// Secondary

async function createSubscription() {
    const response = await libcurl.fetch(
        website + "/api/realtime",
        {
            headers: {
                Authorization: `Bearer ${UserAuth}`,
                Accept: "text/event-stream"
            }
        }
    );

    for await (const event of events(response)) {
        console.log(event);

        if (event.event === "PB_CONNECT") {
            const data = JSON.parse(event.data);

            console.log("Connected:", data.clientId);

            await subscribe(data.clientId);
        }

        if (event.event === "Messages/*") {
            const data = JSON.parse(event.data);

            await SubscriptionExtra(data, data.record);
        }
    }
}

async function SubscriptionExtra(event, eventRecord) {
        const container = document.getElementById("Messages");
      const oldClientHeight = container.clientHeight;
    const oldHeight = container.scrollHeight;
    const oldScrollTop = container.scrollTop;
    if (document.visibilityState !== "visible") {
        console.log("Creating notification", Notification.permission);
        const img = "GroupLink-Fancy.png";
        const text = event.record["Creator"];
        const notification = new Notification(`${event.record["Creator"]} + ${event.record["Message"]}`);
     }
     if (oldScrollTop + oldClientHeight >= oldHeight - 5) {
          await f_createMessage(event.record["Creator"], event.record["Message"]);
          container.scrollTop = container.scrollHeight;
     } else {
          await f_createMessage(event.record["Creator"], event.record["Message"]);
     }
}

async function SubscriptionExtra(event, eventRecord) {
        const container = document.getElementById("Messages");
      const oldClientHeight = container.clientHeight;
     const oldHeight = container.scrollHeight;
     const oldScrollTop = container.scrollTop;
     //alert("Stuff")
           if (document.visibilityState !== "visible") {
            console.log("Creating notification", Notification.permission);
const img = "GroupLink-Fancy.png";
const text = event.record["Creator"]
const notification = new Notification(`${event.record["Creator"]} + ${event.record["Message"]}`);
           }
        if (oldScrollTop + oldClientHeight >= oldHeight - 5) {
              await f_createMessage(event.record["Creator"], event.record["Message"])
              container.scrollTop = container.scrollHeight;
    } else {
                  await f_createMessage(event.record["Creator"], event.record["Message"])


    }
}

async function subscribe(clientId) {
        const body = {
      clientId,
      subscriptions: [
        "Messages/*"
      ]
    }
  const response = await libcurl.fetch(website + "/api/realtime", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${UserAuth}`
      },
      body: JSON.stringify(body)
    });

    if (!response.ok && !response != "204") {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
}

async function createGC(Owner, Name, Avatar){
createGCDiv.style = "visibility: hidden;"
const body = {
    "Owner": Owner, 
    "Name": Name,
    "Icon": Avatar
};
console.log(document.getElementById("GC-Create-Icon").files[0]);
const record = await PostData("Groups",body)

await addToGC(record.id, Owner, "Owner")
}
async function addFriend(UserName) {
  //alert(UserName)
  var friendUserID = await getUserIDFromUsername(UserName)
  //alert(friendUserID)
  const body = {
    "Status": "Pending",
    "Creator": UserID,
    "Receiver": friendUserID
};
//alert(body)
 var record = await PostData("Friends", body)
}
async function removeFriend(userid, otherid) {
  var friendID = await getFriendRecordID(userid, otherid)
  //alert(friendID)
  await DeleteData("Friends", friendID)
}
async function acceptFriend(userid, otherid) {
  var friendID = await getFriendRecordID(userid, otherid)
  //alert(friendID)
  const body = {
    "Status": "Accepted"
};
 var record = await UpdateData(friendID, "Friends", body)
}
async function addToGC(GroupID, UserID, Permission) { 
const body = {
  "User": UserID,
  "Group": GroupID,
  "Permissions": Permission
};
const record = await PostData("GroupUsers",body)
};
async function getPendingFriendRequests(UserID) {
    const filter = encodeURIComponent(
        `Status="Pending" && Receiver="${UserID}"`
    );

    const data = await GetData(
        "Friends",
        "",
        `?filter=${filter}&perPage=500`
    );

    return data.items.map(request => request.Creator);
}
async function getFriends(UserID) {
    const filter = encodeURIComponent(
        `(Status="Accepted") && (Creator="${UserID}" || Receiver="${UserID}")`
    );

    const data = await GetData(
        "Friends",
        "",
        `?filter=${filter}&perPage=500`
    );
    alert(JSON.stringify(data))
    return data.items.map(friend =>
        friend.Creator === UserID
            ? friend.Receiver
            : friend.Creator
    );
}
async function getFriendRecordID(userID, otherUserID) {
    const filter = encodeURIComponent(
        `((Creator="${userID}" && Receiver="${otherUserID}") || (Creator="${otherUserID}" && Receiver="${userID}"))`
    );

    const data = await GetData(
        "Friends",
        "",
        `?filter=${filter}&perPage=1`
    );

    return data.items.length > 0 ? data.items[0].id : null;
}
async function getUserIDFromUsername(username) {
    const filter = encodeURIComponent(
        `Username="${username}"`
    );

    const data = await GetData(
        "users",
        "",
        `?filter=${filter}&perPage=1`
    );

    if (!data.items.length) {
        return null;
    }

    return data.items[0].id;
}


async function sendMessages(GroupID, UserID, Message) { 
const body = {
  "Creator": UserID,
  "Group": GroupID,
  "Message": Message
};

const record = await PostData("Messages",body)
};

async function getUserGroups(UserID) {
    const filter = encodeURIComponent(`User="${UserID}"`);

    const data = await GetData(
        "GroupUsers",
        "",
        `?filter=${filter}&perPage=500`
    );
    //alert(JSON.stringify(data))
    return data.items.map(record => record.Group);
}
async function getGroupUsers(GroupID) {
    const filter = encodeURIComponent(`Group="${GroupID}"`);

    const data = await GetData(
        "GroupUsers",
        "",
        `?filter=${filter}&perPage=500`
    );
    //alert(JSON.stringify(data))
    return data.items.map(record => record.User);
}

async function DeleteGC() {
    //alert("That isn't possible right now, please try again later.")
}

async function KickUser(UserID, GroupChat) {
    const filter = encodeURIComponent(
        `User="${UserID}" && Group="${GroupChat}"`
    );

    const data = await GetData(
        "GroupUsers",
        "",
        `?filter=${filter}&perPage=1`
    );

    if (data.items.length === 0) {
        throw new Error("User is not a member of this group.");
    }

    await DeleteData("GroupUsers", data.items[0].id);
}

async function getGroupInfo(GroupID) {
const record = await GetData("Groups",GroupID)
return(record)
}
async function getFile(collection,record,fileName){
    var response = await libcurl.fetch(website + "/api/files/" + collection + "/" + record + "/" + fileName)
    
    var text = await response.blob()
    return(text)
}

async function getMessages(GroupID, MessageTime) {
  if (!reachedChatEnd){
    const filter = encodeURIComponent(
        `Group="${GroupID}" && created<"${MessageTime}"`
    );
    var response = await GetData(
        "Messages",
        "",
        `?page=1&perPage=50&filter=${filter}&sort=-created`
    );
    response = response["items"]
    currentLatestMessageTime = (response[response.length - 1])["created"]
    console.log(
    "Oldest on page:",
    response.length ? response[response.length - 1].created : "none"
);
console.log(
    "Page:",
    response.map(m => m.id)
);
    response.reverse()
    if (Object.keys(response).length === 0){
      reachedChatEnd = true
        return([])
    }
    return(response)
} else {
  return([])
}

}

async function getUserCache(id) {
if (Object.hasOwn(UserCache, id)){
return(UserCache[id])
} else {
  var response = await GetData("users",id)
  UserCache[id] = response//["items"]
  return(UserCache[id])
}
}


// Main Functions
async function login(username,password) {
  libcurl.set_websocket("wss://anura.pro/");
  try {
    const response = await libcurl.fetch(website + "/api/collections/users/auth-with-password", {
      method: "POST", 
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        "identity": username,
        "password": password
      })
    });
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }

    const data = await response.json();
    console.log("Success:", data);
    UserAuth = data["token"]
    //alert(UserAuth)
    UserID = (data["record"])["id"]
    return(data)
  } catch (error) {
    alert("Error:", error);
  }
}

async function PostData(collection,body,params="") {
  try {
    var formData
    formData = new FormData();
    for (i in body){
      formData.append(i, body[i])
    }
    const response = await libcurl.fetch(website + "/api/collections/" + collection + "/records" + params, {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${UserAuth}`
      },
      body: formData
    });

    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }

    const data = await response.json();
    console.log("Success:", data);
    return(data)
  } catch (error) {
    console.error("Error sending data:", error);
  }
}

async function UpdateData(id,collection,body,params="") {
  try {
    var formData
    formData = new FormData();
    for (i in body){
      formData.append(i, body[i])
    }
    const response = await libcurl.fetch(website + "/api/collections/" + collection + "/records/"  + id + params, {
      method: "PATCH",
      headers: {
        "Authorization": `Bearer ${UserAuth}`
      },
      body: formData
    });

    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }

    const data = await response.json();
    console.log("Success:", data);
    return(data)
  } catch (error) {
    console.error("Error sending data:", error);
  }
}
async function GetData(collection,id="",params="") {
  try {
    var slash
    if (id == ""){
      slash = ""
    } else {
      slash = "/"
    }
    const response = await libcurl.fetch(website + "/api/collections/" + collection + "/records" + slash + id + params, {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${UserAuth}`
      }
    });

    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }

    const data = await response.json();
    console.log("Success:", data);
    return(data)
  } catch (error) {
    console.error("Error sending data:", error);
  }
}
async function DeleteData(collection,id) {
  try {
    const response = await libcurl.fetch(website + "/api/collections/" + collection + "/records/" + id, {
      method: "DELETE",
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${UserAuth}`
      }
    });
   if (response.status === 204) {
    console.log("Deleted Data!")
   }


} catch (error) {
        console.error(error);
    }
}