import Router from "@koa/router";
import { getClient } from "../ApiMastodonCompatibleService.js";
import querystring from "node:querystring";
import qs from "qs";
import { convertTimelinesArgsId, limitToInt } from "./timeline.js";
import { fetchMeta, fromMastodonId, isUnicodeEmoji } from "backend-rs";
import {
	convertAccount,
	convertAttachment,
	convertPoll,
	convertStatus,
} from "../converters.js";
import { apiLogger } from "@/server/api/logger.js";
import { inspect } from "node:util";

function normalizeQuery(data: any) {
	const str = querystring.stringify(data);
	return qs.parse(str);
}

export function apiStatusMastodon(router: Router): void {
	router.post("/v1/statuses", async (ctx) => {
		const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
		const accessTokens = ctx.headers.authorization;
		const client = getClient(BASE_URL, accessTokens);
		try {
			let body: any = ctx.request.body;
			if (body.in_reply_to_id)
				body.in_reply_to_id = fromMastodonId(body.in_reply_to_id);
			if (body.quote_id) body.quote_id = fromMastodonId(body.quote_id);
			if (
				(!body.poll && body["poll[options][]"]) ||
				(!body.media_ids && body["media_ids[]"])
			) {
				body = normalizeQuery(body);
			}
			const text = body.status;
			const removed = text.replace(/@\S+/g, "").replace(/\s|​/g, "");
			const isDefaultEmoji = isUnicodeEmoji(removed);
			const isCustomEmoji = /^:[a-zA-Z0-9@_]+:$/.test(removed);
			if ((body.in_reply_to_id && isDefaultEmoji) || isCustomEmoji) {
				const a = await client.createEmojiReaction(
					body.in_reply_to_id,
					removed,
				);
				ctx.body = a.data;
			}
			if (body.in_reply_to_id && removed === "/unreact") {
				try {
					const id = body.in_reply_to_id;
					const post = await client.getStatus(id);
					const react = post.data.reactions.filter((e) => e.me)[0].name;
					const data = await client.deleteEmojiReaction(id, react);
					ctx.body = data.data;
				} catch (e: any) {
					apiLogger.error(inspect(e));
					ctx.status = 401;
					ctx.body = e.response.data;
				}
			}
			if (!body.media_ids) body.media_ids = undefined;
			if (body.media_ids && !body.media_ids.length) body.media_ids = undefined;
			if (body.media_ids) {
				body.media_ids = (body.media_ids as string[]).map((p) =>
					fromMastodonId(p),
				);
			}
			const { sensitive } = body;
			body.sensitive =
				typeof sensitive === "string" ? sensitive === "true" : sensitive;

			if (body.poll) {
				if (
					body.poll.expires_in != null &&
					typeof body.poll.expires_in === "string"
				)
					body.poll.expires_in = parseInt(body.poll.expires_in);
				if (
					body.poll.multiple != null &&
					typeof body.poll.multiple === "string"
				)
					body.poll.multiple = body.poll.multiple == "true";
				if (
					body.poll.hide_totals != null &&
					typeof body.poll.hide_totals === "string"
				)
					body.poll.hide_totals = body.poll.hide_totals == "true";
			}

			const data = await client.postStatus(text, body);
			ctx.body = convertStatus(data.data);
		} catch (e: any) {
			apiLogger.error(inspect(e));
			ctx.status = 401;
			ctx.body = e.response.data;
		}
	});
	router.get<{ Params: { id: string } }>("/v1/statuses/:id", async (ctx) => {
		const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
		const accessTokens = ctx.headers.authorization;
		const client = getClient(BASE_URL, accessTokens);
		try {
			const data = await client.getStatus(fromMastodonId(ctx.params.id));
			ctx.body = convertStatus(data.data);
		} catch (e: any) {
			apiLogger.error(inspect(e));
			ctx.status = ctx.status == 404 ? 404 : 401;
			ctx.body = e.response.data;
		}
	});
	router.delete<{ Params: { id: string } }>("/v1/statuses/:id", async (ctx) => {
		const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
		const accessTokens = ctx.headers.authorization;
		const client = getClient(BASE_URL, accessTokens);
		try {
			const data = await client.deleteStatus(fromMastodonId(ctx.params.id));
			ctx.body = data.data;
		} catch (e: any) {
			apiLogger.error(inspect(e));
			ctx.status = 401;
			ctx.body = e.response.data;
		}
	});
	interface IReaction {
		id: string;
		createdAt: string;
		user: MisskeyEntity.User;
		type: string;
	}
	router.get<{ Params: { id: string } }>(
		"/v1/statuses/:id/context",
		async (ctx) => {
			const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
			const accessTokens = ctx.headers.authorization;
			const client = getClient(BASE_URL, accessTokens);
			try {
				const id = fromMastodonId(ctx.params.id);
				const data = await client.getStatusContext(
					id,
					convertTimelinesArgsId(limitToInt(ctx.query as any)),
				);

				data.data.ancestors = data.data.ancestors.map((status) =>
					convertStatus(status),
				);
				data.data.descendants = data.data.descendants.map((status) =>
					convertStatus(status),
				);
				ctx.body = data.data;
			} catch (e: any) {
				apiLogger.error(inspect(e));
				ctx.status = 401;
				ctx.body = e.response.data;
			}
		},
	);
	router.get<{ Params: { id: string } }>(
		"/v1/statuses/:id/history",
		async (ctx) => {
			const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
			const accessTokens = ctx.headers.authorization;
			const client = getClient(BASE_URL, accessTokens);
			try {
				const data = await client.getStatusHistory(
					fromMastodonId(ctx.params.id),
				);
				ctx.body = data.data.map((account) => convertAccount(account));
			} catch (e: any) {
				apiLogger.error(inspect(e));
				ctx.status = 401;
				ctx.body = e.response.data;
			}
		},
	);
	router.get<{ Params: { id: string } }>(
		"/v1/statuses/:id/reblogged_by",
		async (ctx) => {
			const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
			const accessTokens = ctx.headers.authorization;
			const client = getClient(BASE_URL, accessTokens);
			try {
				const data = await client.getStatusRebloggedBy(
					fromMastodonId(ctx.params.id),
				);
				ctx.body = data.data.map((account) => convertAccount(account));
			} catch (e: any) {
				apiLogger.error(inspect(e));
				ctx.status = 401;
				ctx.body = e.response.data;
			}
		},
	);
	router.get<{ Params: { id: string } }>(
		"/v1/statuses/:id/favourited_by",
		async (ctx) => {
			const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
			const accessTokens = ctx.headers.authorization;
			const client = getClient(BASE_URL, accessTokens);
			try {
				const data = await client.getStatusFavouritedBy(
					fromMastodonId(ctx.params.id),
				);
				ctx.body = data.data.map((account) => convertAccount(account));
			} catch (e: any) {
				apiLogger.error(inspect(e));
				ctx.status = 401;
				ctx.body = e.response.data;
			}
		},
	);
	router.post<{ Params: { id: string } }>(
		"/v1/statuses/:id/favourite",
		async (ctx) => {
			const meta = await fetchMeta(true);
			const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
			const accessTokens = ctx.headers.authorization;
			const client = getClient(BASE_URL, accessTokens);
			const react = meta.defaultReaction;
			try {
				const a = (await client.createEmojiReaction(
					fromMastodonId(ctx.params.id),
					react,
				)) as any;
				//const data = await client.favouriteStatus(ctx.params.id) as any;
				ctx.body = convertStatus(a.data);
			} catch (e: any) {
				apiLogger.error(inspect(e));
				ctx.status = 401;
				ctx.body = e.response.data;
			}
		},
	);
	router.post<{ Params: { id: string } }>(
		"/v1/statuses/:id/unfavourite",
		async (ctx) => {
			const meta = await fetchMeta(true);
			const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
			const accessTokens = ctx.headers.authorization;
			const client = getClient(BASE_URL, accessTokens);
			const react = meta.defaultReaction;
			try {
				const data = await client.deleteEmojiReaction(
					fromMastodonId(ctx.params.id),
					react,
				);
				ctx.body = convertStatus(data.data);
			} catch (e: any) {
				apiLogger.error(inspect(e));
				ctx.status = 401;
				ctx.body = e.response.data;
			}
		},
	);

	router.post<{ Params: { id: string } }>(
		"/v1/statuses/:id/reblog",
		async (ctx) => {
			const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
			const accessTokens = ctx.headers.authorization;
			const client = getClient(BASE_URL, accessTokens);
			try {
				const data = await client.reblogStatus(fromMastodonId(ctx.params.id));
				ctx.body = convertStatus(data.data);
			} catch (e: any) {
				apiLogger.error(inspect(e));
				ctx.status = 401;
				ctx.body = e.response.data;
			}
		},
	);

	router.post<{ Params: { id: string } }>(
		"/v1/statuses/:id/unreblog",
		async (ctx) => {
			const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
			const accessTokens = ctx.headers.authorization;
			const client = getClient(BASE_URL, accessTokens);
			try {
				const data = await client.unreblogStatus(fromMastodonId(ctx.params.id));
				ctx.body = convertStatus(data.data);
			} catch (e: any) {
				apiLogger.error(inspect(e));
				ctx.status = 401;
				ctx.body = e.response.data;
			}
		},
	);

	router.post<{ Params: { id: string } }>(
		"/v1/statuses/:id/bookmark",
		async (ctx) => {
			const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
			const accessTokens = ctx.headers.authorization;
			const client = getClient(BASE_URL, accessTokens);
			try {
				const data = await client.bookmarkStatus(fromMastodonId(ctx.params.id));
				ctx.body = convertStatus(data.data);
			} catch (e: any) {
				apiLogger.error(inspect(e));
				ctx.status = 401;
				ctx.body = e.response.data;
			}
		},
	);

	router.post<{ Params: { id: string } }>(
		"/v1/statuses/:id/unbookmark",
		async (ctx) => {
			const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
			const accessTokens = ctx.headers.authorization;
			const client = getClient(BASE_URL, accessTokens);
			try {
				const data = await client.unbookmarkStatus(
					fromMastodonId(ctx.params.id),
				);
				ctx.body = convertStatus(data.data);
			} catch (e: any) {
				apiLogger.error(inspect(e));
				ctx.status = 401;
				ctx.body = e.response.data;
			}
		},
	);

	router.post<{ Params: { id: string } }>(
		"/v1/statuses/:id/pin",
		async (ctx) => {
			const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
			const accessTokens = ctx.headers.authorization;
			const client = getClient(BASE_URL, accessTokens);
			try {
				const data = await client.pinStatus(fromMastodonId(ctx.params.id));
				ctx.body = convertStatus(data.data);
			} catch (e: any) {
				apiLogger.error(inspect(e));
				ctx.status = 401;
				ctx.body = e.response.data;
			}
		},
	);

	router.post<{ Params: { id: string } }>(
		"/v1/statuses/:id/unpin",
		async (ctx) => {
			const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
			const accessTokens = ctx.headers.authorization;
			const client = getClient(BASE_URL, accessTokens);
			try {
				const data = await client.unpinStatus(fromMastodonId(ctx.params.id));
				ctx.body = convertStatus(data.data);
			} catch (e: any) {
				apiLogger.error(inspect(e));
				ctx.status = 401;
				ctx.body = e.response.data;
			}
		},
	);

	router.post<{ Params: { id: string; name: string } }>(
		"/v1/statuses/:id/react/:name",
		async (ctx) => {
			const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
			const accessTokens = ctx.headers.authorization;
			const client = getClient(BASE_URL, accessTokens);
			try {
				const data = await client.reactStatus(
					fromMastodonId(ctx.params.id),
					ctx.params.name,
				);
				ctx.body = convertStatus(data.data);
			} catch (e: any) {
				apiLogger.error(inspect(e));
				ctx.status = 401;
				ctx.body = e.response.data;
			}
		},
	);

	router.post<{ Params: { id: string; name: string } }>(
		"/v1/statuses/:id/unreact/:name",
		async (ctx) => {
			const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
			const accessTokens = ctx.headers.authorization;
			const client = getClient(BASE_URL, accessTokens);
			try {
				const data = await client.unreactStatus(
					fromMastodonId(ctx.params.id),
					ctx.params.name,
				);
				ctx.body = convertStatus(data.data);
			} catch (e: any) {
				apiLogger.error(inspect(e));
				ctx.status = 401;
				ctx.body = e.response.data;
			}
		},
	);

	router.get<{ Params: { id: string } }>("/v1/media/:id", async (ctx) => {
		const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
		const accessTokens = ctx.headers.authorization;
		const client = getClient(BASE_URL, accessTokens);
		try {
			const data = await client.getMedia(fromMastodonId(ctx.params.id));
			ctx.body = convertAttachment(data.data);
		} catch (e: any) {
			apiLogger.error(inspect(e));
			ctx.status = 401;
			ctx.body = e.response.data;
		}
	});
	router.put<{ Params: { id: string } }>("/v1/media/:id", async (ctx) => {
		const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
		const accessTokens = ctx.headers.authorization;
		const client = getClient(BASE_URL, accessTokens);
		try {
			const data = await client.updateMedia(
				fromMastodonId(ctx.params.id),
				ctx.request.body as any,
			);
			ctx.body = convertAttachment(data.data);
		} catch (e: any) {
			apiLogger.error(inspect(e));
			ctx.status = 401;
			ctx.body = e.response.data;
		}
	});
	router.get<{ Params: { id: string } }>("/v1/polls/:id", async (ctx) => {
		const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
		const accessTokens = ctx.headers.authorization;
		const client = getClient(BASE_URL, accessTokens);
		try {
			const data = await client.getPoll(fromMastodonId(ctx.params.id));
			ctx.body = convertPoll(data.data);
		} catch (e: any) {
			apiLogger.error(inspect(e));
			ctx.status = 401;
			ctx.body = e.response.data;
		}
	});
	router.post<{ Params: { id: string } }>(
		"/v1/polls/:id/votes",
		async (ctx) => {
			const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
			const accessTokens = ctx.headers.authorization;
			const client = getClient(BASE_URL, accessTokens);
			try {
				const data = await client.votePoll(
					fromMastodonId(ctx.params.id),
					(ctx.request.body as any).choices,
				);
				ctx.body = convertPoll(data.data);
			} catch (e: any) {
				apiLogger.error(inspect(e));
				ctx.status = 401;
				ctx.body = e.response.data;
			}
		},
	);
}
