"use strict";

const dotty = require("dotty");
const unreachable = require("@joepie91/unreachable")("@modular-matrix/client"); // FIXME: Change name when packaging separately

const addCommonFields = require("../event-add-common-fields");
const mapEncryptedFileObject = require("../map-encrypted-file-object");
const stripHTMLReplyFallback = require("../strip-html-reply-fallback");
const stripPlaintextReplyFallback = require("../strip-plaintext-reply-fallback");
const mapMaybeRedacted = require("../map-maybe-redacted");

function mapThumbnail(event) {
	return {
		hasThumbnail: (event.content.info.thumbnail_file != null || event.content.info.thumbnail_url != null),
		thumbnail: {
			filesize: dotty.get(event.content.info, [ "thumbnail_info", "size" ]),
			displayHeight: dotty.get(event.content.info, [ "thumbnail_info", "h" ]),
			displayWidth: dotty.get(event.content.info, [ "thumbnail_info", "w" ]),
			mimetype: dotty.get(event.content.info, [ "thumbnail_info", "mimetype" ]),
			... (event.content.info.thumbnail_file != null)
				? { isEncrypted: true, encryptedFile: mapEncryptedFileObject(event.content.info.thumbnail_file) }
				: { isEncrypted: false, url: event.content.info.thumbnail_url }
		}
	};
}

module.exports = function mapMessageEvent(event, _context) {
	// NOTE: We have some custom redaction handling logic here, separate from the usual `mapMaybeRedacted` logic, because the `m.room.message` event gets split into several different types depending on the msgtype - but that msgtype is *also* redacted when a message is, which means we need a single dedicated "encrypted message" type. This does not mix well with mapMaybeRedacted + the requirement to return an event entirely unchanged when it is of an unrecognized msgtype, as mapMaybeRedacted would *necessarily* always return a new object.
	let isRedacted = dotty.exists(event, ["unsigned", "redacted_because"]);
	
	if (!isRedacted) {	
		// This works around the weird inconsistency that stickers are not `m.room.message` events, despite exactly matching the structure of an `m.room.message` -> `m.image`.
		let messageType = (event.type === "m.sticker")
			? "m.sticker"
			: event.content.msgtype;

		// TODO: Replace with ?. once that is available in all supported Node
		let replyToID = dotty.get(event, [ "content", "m.relates_to", "m.in_reply_to", "event_id" ]);
		let isReply = (replyToID != null);

		let replyFields = (isReply)
			? { inReplyToID: replyToID }
			: {};

		if (messageType === "m.text" || messageType === "m.notice" || messageType === "m.emote") {
			return addCommonFields(event, {
				type: "message",
				sender: event.sender,
				isNotice: (messageType === "m.notice"),
				isEmote: (messageType === "m.emote"),
				text: (isReply === true)
					? stripPlaintextReplyFallback(event.content.body)
					: event.content.body,
				html: (event.content.format === "org.matrix.custom.html")
					? (isReply === true)
						? stripHTMLReplyFallback(event.content.formatted_body)
						: event.content.formatted_body
					: undefined,
				... replyFields,
			});
		} else if (messageType === "m.image" || messageType === "m.sticker") {
			return addCommonFields(event, {
				type: "image",
				sender: event.sender,
				isSticker: (messageType === "m.sticker"),
				description: event.content.body,
				image: {
					filesize: event.content.info.size,
					displayHeight: event.content.info.h,
					displayWidth: event.content.info.w,
					mimetype: event.content.info.mimetype,
					... (event.content.file != null)
						? { isEncrypted: true, encryptedFile: mapEncryptedFileObject(event.content.file) }
						: { isEncrypted: false, url: event.content.url }
				},
				... mapThumbnail(event),
				... replyFields,
			});
		} else if (messageType === "m.video") {
			return addCommonFields(event, {
				type: "video",
				sender: event.sender,
				description: event.content.body,
				video: {
					filesize: event.content.info.size,
					displayHeight: event.content.info.h,
					displayWidth: event.content.info.w,
					mimetype: event.content.info.mimetype,
					... (event.content.file != null)
						? { isEncrypted: true, encryptedFile: mapEncryptedFileObject(event.content.file) }
						: { isEncrypted: false, url: event.content.url }
				},
				... mapThumbnail(event),
				... replyFields,
			});
		} else if (messageType === "m.audio") {
			return addCommonFields(event, {
				type: "audio",
				sender: event.sender,
				description: event.content.body,
				audio: {
					// We nest the audio-related data inside of an object like for m.image, even though we don't have thumbnails to deal with yet, because something like cover art might be added in the future - and this way, we can keep the API consistent in that case.
					filesize: event.content.info.size,
					duration: event.content.info.duration,
					mimetype: event.content.info.mimetype,
					... (event.content.file != null)
						? { isEncrypted: true, encryptedFile: mapEncryptedFileObject(event.content.file) }
						: { isEncrypted: false, url: event.content.url }
				},
				... replyFields,
			});
		} else if (messageType === "m.file") {
			return addCommonFields(event, {
				type: "file",
				sender: event.sender,
				description: event.content.body,
				filename: event.content.filename,
				file: {
					filesize: event.content.info.size,
					mimetype: event.content.info.mimetype,
					... (event.content.file != null)
						? { isEncrypted: true, encryptedFile: mapEncryptedFileObject(event.content.file) }
						: { isEncrypted: false, url: event.content.url }
				},
				... mapThumbnail(event),
				... replyFields,
			});
		} else if (messageType === "m.location") {
			return addCommonFields(event, {
				type: "location",
				sender: event.sender,
				description: event.content.body,
				url: event.content.geo_uri,
				... replyFields,
			});
		} else if (messageType === ":type") {
			// FIXME: Temporary hack to ignore an erroneous testing event, until there's unrecognized-event logging infrastructure
			return null;
		} else {
			// NOTE: The event should remain *completely* unaltered in this case!
			return event;
		}
	} else {
		return addCommonFields({
			sender: event.sender,
			... mapMaybeRedacted(event, {
				redacted: () => ({ type: "redactedMessage" }),
				notRedacted: () => {
					unreachable("Reached notRedacted handler for redacted message event");
				}
			})
		});
	}
};
