// @ts-check

const assert = require("assert/strict")
const Ty = require("../types")
const {Semaphore} = require("@chriscdn/promise-semaphore")
const {tag} = require("@cloudrac3r/html-template-tag")
const {db, sync, select, from} = require("../passthrough")

/** @type {import("./utils")}) */
const utils = sync.require("./utils")

const roomUpgradeSema = new Semaphore()

/**
 * @param {Ty.Event.StateOuter<Ty.Event.M_Room_Tombstone>} event
 * @param {import("./api")} api
 */
async function onTombstone(event, api) {
	// Preconditions (checked by event-dispatcher, enforced here)
	assert.equal(event.state_key, "")
	assert.ok(event.content.replacement_room)

	// Set up
	const oldRoomID = event.room_id
	const newRoomID = event.content.replacement_room
	const channel = select("channel_room", ["name", "channel_id"], {room_id: oldRoomID}).get()
	if (!channel) return
	db.prepare("REPLACE INTO room_upgrade_pending (new_room_id, old_room_id) VALUES (?, ?)").run(newRoomID, oldRoomID)

	// Try joining
	try {
		await api.joinRoom(newRoomID)
	} catch (e) {
		const message = new utils.MatrixStringBuilder()
		message.add(
			`You upgraded the bridged room ${channel.name}. To keep bridging, I need you to invite me to the new room: https://matrix.to/#/${newRoomID}`,
			tag`You upgraded the bridged room <strong>${channel.name}</strong>. To keep bridging, I need you to invite me to the new room: <a href="https://matrix.to/#/${newRoomID}">https://matrix.to/#/${newRoomID}</a>`
		)
		const privateRoomID = await api.usePrivateChat(event.sender)
		await api.sendEvent(privateRoomID, "m.room.message", message.get())
	}

	// Now wait to be invited to/join the room that has the upgrade pending...
}

/**
 * @param {Ty.Event.StateOuter<Ty.Event.M_Room_Member>} event
 * @param {import("./api")} api
 * @param {import("../d2m/actions/create-room")} createRoom
 * @returns {Promise<boolean>} whether to cancel other membership actions
 */
async function onBotMembership(event, api, createRoom) {
	// Preconditions (checked by event-dispatcher, enforced here)
	assert.equal(event.type, "m.room.member")
	assert.equal(event.state_key, utils.bot)

	return await roomUpgradeSema.request(async () => {
		// Check if an upgrade is pending for this room
		const newRoomID = event.room_id
		const oldRoomID = select("room_upgrade_pending", "old_room_id", {new_room_id: newRoomID}).pluck().get()
		if (!oldRoomID) return false
		const channelRow = from("channel_room").join("guild_space", "guild_id").where({room_id: oldRoomID}).select("space_id", "guild_id", "channel_id").get()
		assert(channelRow) // this could only fail if the channel was unbridged or something between upgrade and joining

		// Check if is join/invite
		if (event.content.membership !== "invite" && event.content.membership !== "join") return false

		// If invited, join
		if (event.content.membership === "invite") {
			await api.joinRoom(newRoomID)
		}

		// Remove old room from space
		await api.sendState(channelRow.space_id, "m.space.child", oldRoomID, {})
		// await api.sendState(oldRoomID, "m.space.parent", spaceID, {}) // keep this - the room isn't advertised but should still be grouped if opened

		// Remove declaration that old room is bridged (if able)
		try {
			await api.sendState(oldRoomID, "uk.half-shot.bridge", `moe.cadence.ooye://discord/${channelRow.guild_id}/${channelRow.channel_id}`, {})
		} catch (e) { /* c8 ignore next */ }

		// Update database
		db.transaction(() => {
			db.prepare("DELETE FROM room_upgrade_pending WHERE new_room_id = ?").run(newRoomID)
			db.prepare("UPDATE channel_room SET room_id = ? WHERE channel_id = ?").run(newRoomID, channelRow.channel_id)
			db.prepare("INSERT INTO historical_channel_room (room_id, reference_channel_id, upgraded_timestamp) VALUES (?, ?, ?)").run(newRoomID, channelRow.channel_id, Date.now())
		})()

		// Sync
		await createRoom.syncRoom(channelRow.channel_id)
		return true
	}, event.room_id)
}

module.exports.onTombstone = onTombstone
module.exports.onBotMembership = onBotMembership
