import * as MastodonAPI from "/JS/MastodonAPI.js";
import * as BlueskyAPI from "/JS/BlueskyAPI.js";
import * as TumblrAPI from "/JS/TumblrAPI.js";
import * as Variables from "/JS/Variables.js";

// Buttons
let Favorite = document.getElementsByClassName("Favorite")[0];
let Boost = document.getElementsByClassName("Boost")[0];
let Reply = document.getElementsByClassName("Reply")[0];

let FavoriteFlipper = false;
let BoostFlipper = false;
let Favoritable = false;
let Boostable = false;

// Variables
let website = document.location.href.split("website=")[1];
let post = JSON.parse(localStorage.getItem("post"));
let ThreadedPost = [];

document.getElementsByClassName("Origin")[0].innerHTML = website;

// Other post stuff.
for (let i of document.getElementsByClassName("Handle")) {
	i.onclick = (event) => {
		window.location.href = "/account?website=" + website;
	}
}

GetPost();

// Fixes a bug where the interpreter sucks ass.
let EmbedCounter = 0;

// Button stuff
Favorite.onclick = (event) => {
	if (Favoritable == false) {
		return;
	}
	Favoritee();
}

async function Favoritee() {
	Favoritable = false;
	if (website == "Mastodon") {
		await MastodonAPI.CreateFavorite(post.id, post.favourited);
	} else if (website == "Bluesky") {
		await BlueskyAPI.CreateLike(localStorage.getItem(Variables.BlueskyDID), post.post.uri, post.post.cid);
	}
	SetFavorite();
}

Boost.onclick = (event) => {
	if (Boostable == false) {
		return;
	}
	Boostee();
}

async function Boostee() {
	Boostable = false;
	if (website == "Mastodon") {
		await MastodonAPI.CreateReblog(post.id, post.reblogged);
	} else if (website == "Bluesky") {
		await BlueskyAPI.CreateRepost(localStorage.getItem(Variables.BlueskyDID), post.post.uri, post.post.cid);
	}
	SetBoost();
}

Reply.onclick = (event) => {
	window.location.href = "/post?website=" + website;
}

// Threads are the thing above the post you clicked.
async function SetThreadPost(element, i) {
	if (website == "Mastodon") {
		localStorage.setItem("post", JSON.stringify(element));
	} else if (website == "Bluesky") {
		let Temp = await BlueskyAPI.GetPosts(element.uri);
		element.post = Temp.posts[0];
		localStorage.setItem("post", JSON.stringify(element));
	}
	localStorage.setItem("post", JSON.stringify(element));
	if (i.classList.contains("Handle")) {
		window.location.href = "/account?website=" + website;
	} else {
		window.location.href = "/expanded?website=" + website;
	}
}

// Functions and things.
async function GetPost() {
	if (website == "Mastodon") {
		document.getElementsByClassName("Handle")[0].innerHTML = post.account.acct;
		// Check for a reblog.
		if (post.reblog != null) {
			document.getElementsByClassName("Handle")[0].innerHTML += " ( R: " + post.reblog + " )";
		}
		document.getElementsByClassName("PostText")[0].innerHTML = post.content;
		// Set the texts. It's opposite because "setting" causes it to switch.
		post = await MastodonAPI.GetStatus(post.id);
		FavoriteFlipper = !(post.favourited);
		BoostFlipper = !(post.reblogged);
		SetFavorite();
		SetBoost();
		// Show the image if it exists.
		if (post.media_attachments.length != 0) {
			for (let i of post.media_attachments) {
				ApplyMedia(i, document.getElementsByClassName("Images")[0]);
			}
		}
		// Now time to see if there are any parents.
		let NumberOfThreads = 0;
		let TemporaryPost = post;
		while (TemporaryPost.in_reply_to_id != null) {
			TemporaryPost = await MastodonOtherFunction(NumberOfThreads, TemporaryPost.in_reply_to_id, true);
			ThreadedPost.push(TemporaryPost);
			// Any posts? Give them the thing :3
			for (let i of document.getElementsByClassName(NumberOfThreads)) {
				let TempID = NumberOfThreads;
				i.onclick = (event) => {
					SetThreadPost(ThreadedPost[TempID], i);
				}
			}
			NumberOfThreads += 1;
		}
		// Replies, anyone?
		let Context = await MastodonAPI.GetContexts(post.id);
		for (let i of Context.descendants) {
			TemporaryPost = await MastodonOtherFunction(NumberOfThreads, i, false);
			ThreadedPost.push(TemporaryPost);
			// Any posts? Give them the thing :3
			for (let j of document.getElementsByClassName(NumberOfThreads)) {
				let TempID = NumberOfThreads;
				j.onclick = (event) => {
					SetThreadPost(ThreadedPost[TempID], j);
				}
			}
			NumberOfThreads += 1;
		}
	} else if (website == "Bluesky") {
		// Check for a reblog.
		if (post.hasOwnProperty("reason") && post.reason.$type == "app.bsky.feed.defs#reasonRepost") {
			document.getElementsByClassName("Handle")[0].innerHTML = post.post.author.handle + " ( R: " + post.reason.by.handle + " )";
		} else {
			document.getElementsByClassName("Handle")[0].innerHTML = post.post.author.handle;
		}
		// Text. This will be modified later.
		var Text = await BlueskyAPI.ApplyFacets(post.post.record, post.post.record.text);
		// Place the text.
		Text = Text.replace(/\r?\n|\r/g, "<br/>");
		document.getElementsByClassName("PostText")[0].innerHTML = Text;
		// Show the image if it exists.
		if (post.post.record.hasOwnProperty("embed")) {
			ApplyMedia(post.post.record.embed, document.getElementsByClassName("Images")[0], post.post.author.did);
		}
		// We don't need to update the post with new information. The repos are seperate.
		// Set the texts. It's opposite because "setting" causes it to switch.
		let GetRepo = await BlueskyAPI.GetRecord(localStorage.getItem(Variables.BlueskyDID), "app.bsky.feed.like", post.post.uri.split("/")[post.post.uri.split("/").length - 1]);
		if (GetRepo.hasOwnProperty("error") && GetRepo.error == "RecordNotFound") {
			FavoriteFlipper = true;
		}
		GetRepo = await BlueskyAPI.GetRecord(localStorage.getItem(Variables.BlueskyDID), "app.bsky.feed.repost", post.post.uri.split("/")[post.post.uri.split("/").length - 1]);
		if (GetRepo.hasOwnProperty("error") && GetRepo.error == "RecordNotFound") {
			BoostFlipper = true;
		}
		SetFavorite();
		SetBoost();
		// Now time to see if there are any parents.
		let NumberOfThreads = 0;
		let TemporaryPost = post;
		while (TemporaryPost.hasOwnProperty("reply")) {
			TemporaryPost = await BlueskyOtherFunction(NumberOfThreads, TemporaryPost.reply.parent, true);
			ThreadedPost.push(TemporaryPost);
			// Any posts? Give them the thing :3
			for (let i of document.getElementsByClassName(NumberOfThreads)) {
				let TempID = NumberOfThreads;
				i.onclick = (event) => {
					SetThreadPost(ThreadedPost[TempID], i);
				}
			}
			NumberOfThreads += 1;
		}
		// Replies, anyone?
		let Context = await BlueskyAPI.GetPostThread(post.post.uri);
		for (let i of Context.thread.replies) {
			TemporaryPost = await BlueskyOtherFunction(NumberOfThreads, i.post, false);
			ThreadedPost.push(TemporaryPost);
			// Any posts? Give them the thing :3
			for (let j of document.getElementsByClassName(NumberOfThreads)) {
				let TempID = NumberOfThreads;
				j.onclick = (event) => {
					SetThreadPost(ThreadedPost[TempID], j);
				}
			}
			NumberOfThreads += 1;
		}
	} else {
		document.getElementsByClassName("PostText")[0].innerHTML = "Nothing to load.";
	}
}

// Because of repeat code, we can put it into it's own function. It grabs posts "above" it.
async function MastodonOtherFunction(Id, post, IsThread) {
	let OtherPost;
	if (IsThread == true) {
		OtherPost = await MastodonAPI.GetStatus(post);
	} else {
		OtherPost = post;
	}
	let ShitHTML = "<header> <h1 class=\"Handle " + Id + "\"> " + OtherPost.account.acct + "</h1> <h2 class=\"Origin " + Id + "\"> " + website + "</h2> </header> <p class=\"PostText " + Id + "\"></p> <div class=\"Images " + Id + "\"></div> <hr/>";
	if (IsThread == true) {
		document.getElementsByTagName("body")[0].insertAdjacentHTML("afterbegin", ShitHTML);
	} else {
		document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend", ShitHTML);
	}
	if (OtherPost.spoiler_text != "") {
		document.getElementsByClassName("PostText " + Id)[0].innerHTML = "WARNING: " + OtherPost.spoiler_text;
	} else {
		document.getElementsByClassName("PostText " + Id)[0].innerHTML = OtherPost.content;
		if (OtherPost.media_attachments.length != 0) {
			for (let i of OtherPost.media_attachments) {
				ApplyMedia(i, document.getElementsByClassName("Images " + Id));
			}
		}
	}
	return OtherPost;
}

// Because of repeat code, we can put it into it's own function. It grabs posts "above" it.
async function BlueskyOtherFunction(Id, post, IsThread) {
	let OtherPost = await BlueskyAPI.GetPosts([post.uri]);
	OtherPost = OtherPost.posts[0];
	let ShitHTML = "<header> <h1 class=\"Handle " + Id + "\"> " + OtherPost.author.handle + "</h1> <h2 class=\"Origin " + Id + "\"> " + website + "</h2> </header> <p class=\"PostText " + Id + "\"></p> <div class=\"Images " + Id + "\"></div> <hr/>";
	if (IsThread == true) {
		document.getElementsByTagName("body")[0].insertAdjacentHTML("afterbegin", ShitHTML);
	} else {
		document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend", ShitHTML);
	}
	Text = BlueskyAPI.ApplyFacets(OtherPost.record, OtherPost.record.text);
	Text = Text.replace(/\r?\n|\r/g, "<br/>");
	// Sensitive topic :3
	if (OtherPost.record.hasOwnProperty("labels") && OtherPost.record.labels.length != 0) {
		document.getElementsByClassName("PostText " + Id)[0].innerHTML = "LABELS APPLIED: ";
		for (let lab of OtherPost.value.labels.values) {
			document.getElementsByClassName("PostText " + Id)[0].innerHTML += lab.val + " ";
		}
	} else {
		document.getElementsByClassName("PostText " + Id)[0].innerHTML = Text;
		if (OtherPost.record.hasOwnProperty("embed")) {
			ApplyMedia(OtherPost.record.embed, document.getElementsByClassName("Images " + Id)[0], OtherPost.author.did);
		}
	}
	// Get the reply and make it make sense.
	if (OtherPost.record.hasOwnProperty("reply")) {
		OtherPost.reply = OtherPost.record.reply;
	}
	return OtherPost;
}

// Applyers. Essentially applies a thing to an operation.
// Finds any associated media and applies it to the post.
async function ApplyMedia(Media, Element, Author = undefined) {
	// Check to see if the image is on the same server.
	if (website == "Mastodon") {
		if (Media.type == "image") {
			if (Media.remote_url == null) {
				Element.innerHTML += "<img src=" + Media.url + " alt=\"" + EscapeRegExp(Media.description) + "\"/>";
			} else {
				Element.innerHTML += "<img src=" + Media.remote_url + " alt=\"" + EscapeRegExp(Media.description) + "\"/>";
			}
		} else if (Media.type == "video" || Media.type == "gifv") {
			if (Media.remote_url == null) {
				Element.innerHTML += "<video controls src=" + Media.url + " alt=\"" + EscapeRegExp(Media.description) + "\"></video>";
			} else {
				Element.innerHTML += "<video controls src=" + Media.remote_url + " alt=\"" + EscapeRegExp(Media.description) + "\"></video>";
			}
		}
	} else if (website == "Bluesky") {
		// Record and media embed; They are from seperate posts.
		if (Media.$type == "app.bsky.embed.recordWithMedia") {
			await BlueskyBlobEmbed(Media.media, Element, Author);
			let Texty = await BlueskyAPI.GetPosts([Media.record.record.uri]);
			let Text = BlueskyAPI.ApplyFacets(Texty.posts[0].record, Texty.posts[0].record.text);
			Text = Text.replace(/\r?\n|\r/g, "<br/>");
			Element.innerHTML += "<p class='Embed'>" + Text + "</p><br/>";
			// To stop confusion: this is for the RECORD EMBED! It gets an image from the record embed and puts it there.
			if (Texty.posts[0].record.embed.$type == "app.bsky.embed.images" || Texty.posts[0].record.embed.$type == "app.bsky.embed.video") {
				await BlueskyBlobEmbed(Texty.posts[0].record.embed, Element, Texty.posts[0].author.did);
			}
		// Just a record on it's own.
		} else if (Media.$type == "app.bsky.embed.record") {
			let Texty = await BlueskyAPI.GetPosts([Media.record.uri]);
			let Text = BlueskyAPI.ApplyFacets(Texty.posts[0].record, Texty.posts[0].record.text);
			Text = Text.replace(/\r?\n|\r/g, "<br/>");
			Element.innerHTML += "<p class='Embed'>" + Text + "</p><br/>";
			if (Texty.posts[0].record.embed.$type == "app.bsky.embed.images" || Texty.posts[0].record.embed.$type == "app.bsky.embed.video") {
				await BlueskyBlobEmbed(Texty.posts[0].record.embed, Element, Texty.posts[0].author.did);
			}
		// Image on it's own
		} else if (Media.$type == "app.bsky.embed.images" || Media.$type == "app.bsky.embed.video") {
			await BlueskyBlobEmbed(Media, Element, Author);
		}
		EmbedCounter = 0;
	}
}

// Fucking hell.
async function BlueskyBlobEmbed(Blob, Element, Author) {
	if (Blob.$type == "app.bsky.embed.images") {
		for (let i of Blob.images) {
			Element.innerHTML += "<img src=\"\" alt=\"\"/>";
			var Blobby = await BlueskyAPI.GetBlob(Author, i.image.ref.$link);
			var ObjectURL = URL.createObjectURL(Blobby);
			let TempElement = Element.getElementsByTagName("img")[EmbedCounter];
			EmbedCounter += 1;
			TempElement.setAttribute("src", ObjectURL);
			TempElement.setAttribute("alt", EscapeRegExp(i.alt));
		}
	// Video embed.
	} else if (Blob.$type == "app.bsky.embed.video") {
		Element.innerHTML += "<video controls src=\"\">Loading...</video>";
		Element = Element.getElementsByTagName("video")[0];
		var Blobby = await BlueskyAPI.GetBlob(Author, Blob.video.ref.$link);
		var ObjectURL = URL.createObjectURL(Blobby);
		Element.setAttribute("src", ObjectURL);
		Element.innerHTML = "";
	}
}

// Setters
function SetFavorite() {
	FavoriteFlipper = !(FavoriteFlipper);
	if (FavoriteFlipper == false) {
		Favorite.innerHTML = "Favorite!";
	} else {
		Favorite.innerHTML = "Unfavorite...";
	}
	Favoritable = true;
}

function SetBoost() {
	BoostFlipper = !(BoostFlipper);
	if (BoostFlipper == false) {
		Boost.innerHTML = "Boost!";
	} else {
		Boost.innerHTML = "Unboost...";
	}
	Boostable = true;
}

// Functions stolen form elsewhere
function EscapeRegExp(text) {
	if (text == null) {
		return null;
	}
	return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\$&');
}
