import {
	ButtonStyles,
	ChannelTypes,
	ComponentTypes,
	MessageFlags,
	Shard
} from "oceanic.js";
import { client } from ".";
import { Constants } from "./Constants";
import { db } from "./utils/database";
import {
	ActionRow,
	Button,
	ComponentMessage,
	Container,
	Divider,
	Section,
	Separator,
	TextDisplay
} from "~/components";
import { selfappReq } from "./utils/selfappReq";
import { User } from "./components/User";
import { Response } from "./utils/types";
import { match } from "./utils/utils";
import { PendingApplicationMessage } from "./components/PendingApplicationMessage";
import { ApplicationContent } from "./components/ApplicationContent";

import("./joinRequestActions/lockUnlock");
import("./joinRequestActions/interview");
import("./joinRequestActions/accept");

export async function setupJoinRequestHandler(shard: Shard) {
	shard.ws?.on("message", async (d) => {
		const data = JSON.parse(d.toString("utf8"));
		if (!data.t) return;
		if (data.t.startsWith("GUILD_JOIN_REQUEST")) {
			const applicationData = data.d;
			switch (data.t) {
				case "GUILD_JOIN_REQUEST_UPDATE": {
					switch (applicationData.status) {
						case "SUBMITTED": {
							const rawResponses =
								applicationData.request.form_responses;
							const response: Response = {
								hereFor: match(rawResponses[0].response, {
									0: ["tag"],
									1: ["chat"],
									2: ["tag", "chat"]
								}),
								foundThrough: match(rawResponses[1].response, {
									0: "archive",
									1: "user",
									2: "tag",
									3: "other",
									4: "sold"
								}),
								languages: rawResponses[2].response,
								details: rawResponses[3].response,
								canBoost: rawResponses[4].response === 0
							};
							const user = await client.rest.users.get(
								applicationData.request.user.id
							);
							const thread =
								await client.rest.channels.startThreadWithoutMessage(
									Constants.PENDING_CHANNEL_ID,
									{
										name: `${user.tag}'s application`,
										type: ChannelTypes.PUBLIC_THREAD,
										autoArchiveDuration: 10080
									}
								);

							const pendingMsg =
								await client.rest.channels.createMessage(
									Constants.PENDING_CHANNEL_ID,
									<PendingApplicationMessage
										id={applicationData.request.id}
										response={response}
										user={user}
										locked={
											response.foundThrough === "sold"
										}
										threadID={thread.id}
									/>
								);

							const fullApplicantProfile: any = await selfappReq(
								`/users/${user.id}/profile`,
								"GET",
								{
									join_request_id: applicationData.request.id
								}
							);

							thread.createMessage({
								embeds: [
									{
										color: Constants.COLORS.NEW_REQ,
										title: "User bio",
										description:
											"```\n" +
											fullApplicantProfile.user_profile
												.bio +
											"\n```"
									}
								]
							});

							await db.run(
								"INSERT INTO applications (id, user_id, status, message_id, channel_id, responses, thread_id) VALUES (?, ?, 0, ?, ?, ?, ?)",
								applicationData.request.id,
								applicationData.request.user_id,
								pendingMsg.id,
								pendingMsg.channelID,
								JSON.stringify(response),
								thread.id
							);

							break;
						}
					}
					break;
				}
				case "GUILD_JOIN_REQUEST_DELETE": {
					const application = await db.get(
						"SELECT * FROM applications WHERE id=?",
						applicationData.id
					);
					const user = await client.rest.users.get(
						applicationData.user_id
					);

					if (application) {
						await db.run(
							"DELETE FROM applications WHERE id=?",
							applicationData.id
						);
						await client.rest.channels.deleteMessage(
							application.channel_id,
							application.message_id
						);
					}

					const response: Response = JSON.parse(
						application ? application.responses : {} // this will never be used if no applications
					);

					const msg = await client.rest.channels.createMessage(
						Constants.REJECTION_CHANNEL_ID,
						<ComponentMessage>
							<Container accentColor={Constants.COLORS.BAD}>
								<TextDisplay>
									## Join request withdrawn
								</TextDisplay>
								<Divider />
								<User user={user} />
								<Divider />

								{application ? (
									<ApplicationContent
										response={response}
										includeDetails={true}
									/>
								) : (
									<TextDisplay>
										### Application
										<br />
										User hasn't applied
									</TextDisplay>
								)}
							</Container>
						</ComponentMessage>
					);

					await client.rest.request({
						auth: `Bot ${process.env.BOT_TOKEN}`,
						method: "POST",
						path: `/channels/${application.thread_id}/messages`,
						json: {
							content: "",
							flags: 0,
							message_reference: {
								channel_id: msg.channelID,
								guild_id: msg.guildID!,
								message_id: msg.id,
								type: 1
							}
						}
					});

					if (application)
						await client.rest.channels.edit(application.thread_id, {
							locked: true,
							reason: `${user.tag}'s application has been withdrawn`
						});
					break;
				}
			}
		}
	});
}
