// @ts-check

const {tryToCatch} = require("try-to-catch")
const {router, test} = require("../../../test/web")
const {MatrixServerError} = require("../../matrix/mreq")
const {select, db} = require("../../passthrough")
const assert = require("assert").strict

test("web link space: access denied when not logged in to Discord", async t => {
	const [error] = await tryToCatch(() => router.test("post", "/api/link-space", {
		sessionData: {
		},
		body: {
			space_id: "!zTMspHVUBhFLLSdmnS:cadence.moe",
			guild_id: "665289423482519565"
		}
	}))
	t.equal(error.data, "Can't edit a guild you don't have Manage Server permissions in")
})

test("web link space: access denied when not logged in to Matrix", async t => {
	const [error] = await tryToCatch(() => router.test("post", "/api/link-space", {
		sessionData: {
			managedGuilds: ["665289423482519565"]
		},
		body: {
			space_id: "!zTMspHVUBhFLLSdmnS:cadence.moe",
			guild_id: "665289423482519565"
		}
	}))
	t.equal(error.data, "Can't link with your Matrix space if you aren't logged in to Matrix")
})

test("web link space: access denied when bot was invited by different user", async t => {
	const [error] = await tryToCatch(() => router.test("post", "/api/link-space", {
		sessionData: {
			managedGuilds: ["665289423482519565"],
			mxid: "@user:example.org"
		},
		body: {
			space_id: "!zTMspHVUBhFLLSdmnS:cadence.moe",
			guild_id: "665289423482519565"
		}
	}))
	t.equal(error.data, "You personally must invite OOYE to that space on Matrix")
})

test("web link space: access denied when guild is already in use", async t => {
	const [error] = await tryToCatch(() => router.test("post", "/api/link-space", {
		sessionData: {
			managedGuilds: ["112760669178241024"],
			mxid: "@cadence:cadence.moe"
		},
		body: {
			space_id: "!jjmvBegULiLucuWEHU:cadence.moe",
			guild_id: "112760669178241024"
		}
	}))
	t.equal(error.data, "Guild ID 112760669178241024 or space ID !jjmvBegULiLucuWEHU:cadence.moe are already bridged and cannot be reused")
})

test("web link space: check that OOYE is joined", async t => {
	let called = 0
	const [error] = await tryToCatch(() => router.test("post", "/api/link-space", {
		sessionData: {
			managedGuilds: ["665289423482519565"],
			mxid: "@cadence:cadence.moe"
		},
		body: {
			space_id: "!zTMspHVUBhFLLSdmnS:cadence.moe",
			guild_id: "665289423482519565"
		},
		api: {
			async joinRoom(roomID) {
				called++
				throw new MatrixServerError({errcode: "M_FORBIDDEN", error: "not allowed to join I guess"})
			}
		}
	}))
	t.equal(error.data, "Unable to join the requested Matrix space. Please invite the bridge to the space and try again. (Server said: M_FORBIDDEN - not allowed to join I guess)")
	t.equal(called, 1)
})

test("web link space: check that OOYE has PL 100 (not 50)", async t => {
	let called = 0
	const [error] = await tryToCatch(() => router.test("post", "/api/link-space", {
		sessionData: {
			managedGuilds: ["665289423482519565"],
			mxid: "@cadence:cadence.moe"
		},
		body: {
			space_id: "!zTMspHVUBhFLLSdmnS:cadence.moe",
			guild_id: "665289423482519565"
		},
		api: {
			async joinRoom(roomID) {
				called++
				return roomID
			},
			async getStateEvent(roomID, type, key) {
				called++
				t.equal(roomID, "!zTMspHVUBhFLLSdmnS:cadence.moe")
				t.equal(type, "m.room.power_levels")
				t.equal(key, "")
				return {users: {"@_ooye_bot:cadence.moe": 50}}
			},
			async getStateEventOuter(roomID, type, key) {
				called++
				t.equal(roomID, "!zTMspHVUBhFLLSdmnS:cadence.moe")
				t.equal(type, "m.room.create")
				t.equal(key, "")
				return {
					type: "m.room.create",
					state_key: "",
					sender: "@creator:cadence.moe",
					room_id: "!zTMspHVUBhFLLSdmnS:cadence.moe",
					event_id: "$create",
					origin_server_ts: 0,
					content: {
						room_version: "11"
					}
				}
			}
		}
	}))
	t.equal(error.data, "OOYE needs power level 100 (admin) in the target Matrix space")
	t.equal(called, 3)
})

test("web link space: check that inviting user has PL 50", async t => {
	let called = 0
	const [error] = await tryToCatch(() => router.test("post", "/api/link-space", {
		sessionData: {
			managedGuilds: ["665289423482519565"],
			mxid: "@cadence:cadence.moe"
		},
		body: {
			space_id: "!zTMspHVUBhFLLSdmnS:cadence.moe",
			guild_id: "665289423482519565"
		},
		api: {
			async joinRoom(roomID) {
				called++
				return roomID
			},
			async getStateEvent(roomID, type, key) {
				called++
				t.equal(roomID, "!zTMspHVUBhFLLSdmnS:cadence.moe")
				t.equal(type, "m.room.power_levels")
				t.equal(key, "")
				return {users: {"@_ooye_bot:cadence.moe": 100}, events: {"m.room.tombstone": 150}}
			},
			async getStateEventOuter(roomID, type, key) {
				called++
				t.equal(roomID, "!zTMspHVUBhFLLSdmnS:cadence.moe")
				t.equal(type, "m.room.create")
				t.equal(key, "")
				return {
					type: "m.room.create",
					state_key: "",
					sender: "@creator:cadence.moe",
					room_id: "!zTMspHVUBhFLLSdmnS:cadence.moe",
					event_id: "$create",
					origin_server_ts: 0,
					content: {
						room_version: "12"
					}
				}
			}
		}
	}))
	t.equal(error.data, "You need to be at least power level 50 (moderator) in the target Matrix space to set up OOYE, but you are currently power level 0.")
	t.equal(called, 3)
})

test("web link space: successfully adds entry to database and loads page", async t => {
	let called = 0
	await router.test("post", "/api/link-space", {
		sessionData: {
			managedGuilds: ["665289423482519565"],
			mxid: "@cadence:cadence.moe"
		},
		body: {
			space_id: "!zTMspHVUBhFLLSdmnS:cadence.moe",
			guild_id: "665289423482519565"
		},
		api: {
			async joinRoom(roomID) {
				called++
				return roomID
			},
			async getStateEvent(roomID, type, key) {
				called++
				t.equal(roomID, "!zTMspHVUBhFLLSdmnS:cadence.moe")
				t.equal(type, "m.room.power_levels")
				t.equal(key, "")
				return {users: {"@cadence:cadence.moe": 50}}
			},
			async getStateEventOuter(roomID, type, key) {
				called++
				t.equal(roomID, "!zTMspHVUBhFLLSdmnS:cadence.moe")
				t.equal(type, "m.room.create")
				t.equal(key, "")
				return {
					type: "m.room.create",
					state_key: "",
					sender: "@_ooye_bot:cadence.moe",
					room_id: "!zTMspHVUBhFLLSdmnS:cadence.moe",
					event_id: "$create",
					origin_server_ts: 0,
					content: {
						room_version: "12"
					}
				}
			}
		}
	})
	t.equal(called, 3)

	// check that the entry was added to the database
	t.equal(select("guild_space", "privacy_level", {guild_id: "665289423482519565", space_id: "!zTMspHVUBhFLLSdmnS:cadence.moe"}).pluck().get(), 0)

	// check that the guild info page now loads
	const html = await router.test("get", "/guild?guild_id=665289423482519565", {
		sessionData: {
			managedGuilds: ["665289423482519565"],
			mxid: "@cadence:cadence.moe"
		},
		api: {
			async getFullHierarchy(spaceID) {
				return []
			}
		}
	})
	t.has(html, `<h1 class="s-page-title--header">Data Horde</h1>`)
})

// *****

test("web link room: access denied when not logged in to Discord", async t => {
	const [error] = await tryToCatch(() => router.test("post", "/api/link", {
		sessionData: {
		},
		body: {
			discord: "665310973967597573",
			matrix: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
			guild_id: "665289423482519565"
		}
	}))
	t.equal(error.data, "Can't edit a guild you don't have Manage Server permissions in")
})

test("web link room: check that guild exists", async t => {
	const [error] = await tryToCatch(() => router.test("post", "/api/link", {
		sessionData: {
			managedGuilds: ["1"]
		},
		body: {
			discord: "665310973967597573",
			matrix: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
			guild_id: "1"
		}
	}))
	t.equal(error.data, "Discord guild does not exist or bot has not joined it")
})

test("web link room: check that channel exists", async t => {
	const [error] = await tryToCatch(() => router.test("post", "/api/link", {
		sessionData: {
			managedGuilds: ["665289423482519565"]
		},
		body: {
			discord: "1",
			matrix: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
			guild_id: "665289423482519565"
		}
	}))
	t.equal(error.data, "Discord channel does not exist")
})

test("web link room: check that channel is part of guild", async t => {
	const [error] = await tryToCatch(() => router.test("post", "/api/link", {
		sessionData: {
			managedGuilds: ["665289423482519565"]
		},
		body: {
			discord: "112760669178241024",
			matrix: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
			guild_id: "665289423482519565"
		}
	}))
	t.equal(error.data, "Channel ID 112760669178241024 is not part of guild 665289423482519565")
})

test("web link room: check that channel is not already linked", async t => {
	const [error] = await tryToCatch(() => router.test("post", "/api/link", {
		sessionData: {
			managedGuilds: ["112760669178241024"]
		},
		body: {
			discord: "112760669178241024",
			matrix: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
			guild_id: "112760669178241024"
		}
	}))
	t.equal(error.data, "Channel ID 112760669178241024 or room ID !NDbIqNpJyPvfKRnNcr:cadence.moe are already bridged and cannot be reused")
})

test("web link room: checks the autocreate setting if the space doesn't exist yet", async t => {
	let called = 0
	const [error] = await tryToCatch(() => router.test("post", "/api/link", {
		sessionData: {
			managedGuilds: ["665289423482519565"]
		},
		body: {
			discord: "665310973967597573",
			matrix: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
			guild_id: "665289423482519565"
		},
		createSpace: {
			async ensureSpace(guild) {
				called++
				t.equal(guild.id, "665289423482519565")
				// simulate what ensureSpace is intended to check
				const autocreate = 0
				assert.equal(autocreate, 1, "refusing to implicitly create a space for guild 665289423482519565. set the guild_active data first before calling ensureSpace/syncSpace.")
				return ""
			}
		}
	}))
	t.match(error.message, /refusing to implicitly create a space/)
	t.equal(called, 1)
})

test("web link room: check that room is part of space (not in hierarchy)", async t => {
	let called = 0
	const [error] = await tryToCatch(() => router.test("post", "/api/link", {
		sessionData: {
			managedGuilds: ["665289423482519565"]
		},
		body: {
			discord: "665310973967597573",
			matrix: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
			guild_id: "665289423482519565"
		},
		api: {
			async *generateFullHierarchy(spaceID) {
				called++
				t.equal(spaceID, "!zTMspHVUBhFLLSdmnS:cadence.moe")
			}
		}
	}))
	t.equal(error.data, "Matrix room needs to be part of the bridged space")
	t.equal(called, 1)
})

test("web link room: check that bridge can join room (notices lack of via and asks for invite instead)", async t => {
	let called = 0
	const [error] = await tryToCatch(() => router.test("post", "/api/link", {
		sessionData: {
			managedGuilds: ["665289423482519565"]
		},
		body: {
			discord: "665310973967597573",
			matrix: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
			guild_id: "665289423482519565"
		},
		api: {
			async joinRoom(roomID) {
				called++
				throw new MatrixServerError({errcode: "M_FORBIDDEN", error: "not allowed to join I guess"})
			},
			async *generateFullHierarchy(spaceID) {
				called++
				t.equal(spaceID, "!zTMspHVUBhFLLSdmnS:cadence.moe")
				yield {
					room_id: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
					children_state: [],
					guest_can_join: false,
					num_joined_members: 2
				}
				/* c8 ignore next */
			}
		}
	}))
	t.equal(error.data, "Unable to join the requested Matrix room. Please invite the bridge to the room and try again. (Server said: M_FORBIDDEN - not allowed to join I guess)")
	t.equal(called, 2)
})

test("web link room: check that bridge can join room (uses via for join attempt)", async t => {
	let called = 0
	const [error] = await tryToCatch(() => router.test("post", "/api/link", {
		sessionData: {
			managedGuilds: ["665289423482519565"]
		},
		body: {
			discord: "665310973967597573",
			matrix: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
			guild_id: "665289423482519565"
		},
		api: {
			async joinRoom(roomID, _, via) {
				called++
				t.deepEqual(via, ["cadence.moe", "hashi.re"])
				throw new MatrixServerError({errcode: "M_FORBIDDEN", error: "not allowed to join I guess"})
			},
			async *generateFullHierarchy(spaceID) {
				called++
				t.equal(spaceID, "!zTMspHVUBhFLLSdmnS:cadence.moe")
				yield {
					room_id: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
					children_state: [],
					guest_can_join: false,
					num_joined_members: 2
				}
				yield {
					room_id: "!zTMspHVUBhFLLSdmnS:cadence.moe",
					children_state: [{
						type: "m.space.child",
						state_key: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
						sender: "@elliu:hashi.re",
						content: {
							via: ["cadence.moe", "hashi.re"]
						},
						origin_server_ts: 0
					}],
					guest_can_join: false,
					num_joined_members: 2
				}
				/* c8 ignore next */
			}
		}
	}))
	t.equal(error.data, "M_FORBIDDEN - not allowed to join I guess")
	t.equal(called, 2)
})

test("web link room: check that room is not encrypted", async t => {
let called = 0
	const [error] = await tryToCatch(() => router.test("post", "/api/link", {
		sessionData: {
			managedGuilds: ["665289423482519565"]
		},
		body: {
			discord: "665310973967597573",
			matrix: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
			guild_id: "665289423482519565"
		},
		api: {
			async joinRoom(roomID) {
				called++
				return roomID
			},
			async *generateFullHierarchy(spaceID) {
				called++
				t.equal(spaceID, "!zTMspHVUBhFLLSdmnS:cadence.moe")
				yield {
					room_id: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
					children_state: [],
					guest_can_join: false,
					num_joined_members: 2
				}
				/* c8 ignore next */
			},
			async getStateEvent(roomID, type, key) {
				called++
				t.equal(roomID, "!NDbIqNpJyPvfKRnNcr:cadence.moe")
				if (type === "m.room.encryption" && key === "") {
					return {algorithm: "m.megolm.v1.aes-sha2"}
				}
				throw new Error("Unknown state event")
			}
		}
	}))
	t.equal(error.data, "Encrypted rooms are not supported for bridging. Please replace it with an unencrypted room.")
	t.equal(called, 3)
})

test("web link room: check that bridge has PL 100 in target room", async t => {
	let called = 0
	const [error] = await tryToCatch(() => router.test("post", "/api/link", {
		sessionData: {
			managedGuilds: ["665289423482519565"]
		},
		body: {
			discord: "665310973967597573",
			matrix: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
			guild_id: "665289423482519565"
		},
		api: {
			async joinRoom(roomID) {
				called++
				return roomID
			},
			async *generateFullHierarchy(spaceID) {
				called++
				t.equal(spaceID, "!zTMspHVUBhFLLSdmnS:cadence.moe")
				yield {
					room_id: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
					children_state: [],
					guest_can_join: false,
					num_joined_members: 2
				}
				/* c8 ignore next */
			},
			async getStateEvent(roomID, type, key) {
				called++
				t.equal(roomID, "!NDbIqNpJyPvfKRnNcr:cadence.moe")
				if (type === "m.room.power_levels" && key === "") {
					return {users_default: 50}
				}
				throw new Error("Unknown state event")
			},
			async getStateEventOuter(roomID, type, key) {
				called++
				t.equal(roomID, "!NDbIqNpJyPvfKRnNcr:cadence.moe")
				t.equal(type, "m.room.create")
				t.equal(key, "")
				return {
					type: "m.room.create",
					state_key: "",
					sender: "@creator:cadence.moe",
					room_id: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
					event_id: "$create",
					origin_server_ts: 0,
					content: {
						room_version: "11"
					}
				}
			}
		}
	}))
	t.equal(error.data, "OOYE needs power level 100 (admin) in the target Matrix room")
	t.equal(called, 5)
})

test("web link room: successfully calls createRoom", async t => {
	let called = 0
	await router.test("post", "/api/link", {
		sessionData: {
			managedGuilds: ["665289423482519565"]
		},
		body: {
			discord: "665310973967597573",
			matrix: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
			guild_id: "665289423482519565"
		},
		api: {
			async joinRoom(roomID) {
				called++
				return roomID
			},
			async *generateFullHierarchy(spaceID) {
				called++
				t.equal(spaceID, "!zTMspHVUBhFLLSdmnS:cadence.moe")
				yield {
					room_id: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
					children_state: [],
					guest_can_join: false,
					num_joined_members: 2
				}
				/* c8 ignore next */
			},
			async getStateEvent(roomID, type, key) {
				if (type === "m.room.power_levels") {
					called++
					t.equal(roomID, "!NDbIqNpJyPvfKRnNcr:cadence.moe")
					t.equal(key, "")
					return {users: {"@_ooye_bot:cadence.moe": 100}}
				} else if (type === "m.room.name") {
					called++
					t.equal(roomID, "!NDbIqNpJyPvfKRnNcr:cadence.moe")
					return {}
				} else if (type === "m.room.avatar") {
					called++
					t.equal(roomID, "!NDbIqNpJyPvfKRnNcr:cadence.moe")
					return {}
				} else if (type === "m.room.topic") {
					called++
					t.equal(roomID, "!NDbIqNpJyPvfKRnNcr:cadence.moe")
					return {}
				}
			},
			async getStateEventOuter(roomID, type, key) {
				called++
				t.equal(roomID, "!NDbIqNpJyPvfKRnNcr:cadence.moe")
				t.equal(type, "m.room.create")
				t.equal(key, "")
				return {
					type: "m.room.create",
					state_key: "",
					sender: "@creator:cadence.moe",
					room_id: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
					event_id: "$create",
					origin_server_ts: 0,
					content: {
						room_version: "11"
					}
				}
			},
			async sendEvent(roomID, type, content) {
				called++
				t.equal(roomID, "!NDbIqNpJyPvfKRnNcr:cadence.moe")
				t.equal(type, "m.room.message")
				t.match(content.body, /👋/)
				return ""
			}
		},
		createRoom: {
			async syncRoom(channelID) {
				called++
				t.equal(channelID, "665310973967597573")
				return "!NDbIqNpJyPvfKRnNcr:cadence.moe"
			}
		}
	})
	t.equal(called, 9)
})

// *****

test("web unlink room: access denied if not logged in to Discord", async t => {
	const [error] = await tryToCatch(() => router.test("post", "/api/unlink", {
		body: {
			channel_id: "665310973967597573",
			guild_id: "665289423482519565"
		}
	}))
	t.equal(error.data, "Can't edit a guild you don't have Manage Server permissions in")
})

test("web unlink room: checks that guild exists", async t => {
	const [error] = await tryToCatch(() => router.test("post", "/api/unlink", {
		sessionData: {
			managedGuilds: ["2"]
		},
		body: {
			channel_id: "665310973967597573",
			guild_id: "2"
		}
	}))
	t.equal(error.data, "Discord guild does not exist or bot has not joined it")
})

test("web unlink room: checks that the channel is part of the guild", async t => {
	const [error] = await tryToCatch(() => router.test("post", "/api/unlink", {
		sessionData: {
			managedGuilds: ["665289423482519565"]
		},
		body: {
			channel_id: "112760669178241024",
			guild_id: "665289423482519565"
		}
	}))
	t.equal(error.data, "Channel ID 112760669178241024 is not part of guild 665289423482519565")
})

test("web unlink room: successfully calls unbridgeChannel when the channel does exist", async t => {
	let called = 0
	await router.test("post", "/api/unlink", {
		sessionData: {
			managedGuilds: ["665289423482519565"]
		},
		body: {
			channel_id: "665310973967597573",
			guild_id: "665289423482519565"
		},
		createRoom: {
			async unbridgeChannel(channel) {
				called++
				t.equal(channel.id, "665310973967597573")
			}
		}
	})
	t.equal(called, 1)
})

test("web unlink room: successfully calls unbridgeChannel when the channel does not exist", async t => {
	let called = 0
	await router.test("post", "/api/unlink", {
		sessionData: {
			managedGuilds: ["112760669178241024"]
		},
		body: {
			channel_id: "489237891895768942",
			guild_id: "112760669178241024"
		},
		createRoom: {
			async unbridgeChannel(channel) {
				called++
				t.equal(channel.id, "489237891895768942")
			}
		}
	})
	t.equal(called, 1)
})

test("web unlink room: checks that the channel is bridged", async t => {
	const row = db.prepare("SELECT * FROM channel_room WHERE channel_id = '665310973967597573'").get()
	db.prepare("DELETE FROM channel_room WHERE channel_id = '665310973967597573'").run()

	const [error] = await tryToCatch(() => router.test("post", "/api/unlink", {
		sessionData: {
			managedGuilds: ["665289423482519565"]
		},
		body: {
			channel_id: "665310973967597573",
			guild_id: "665289423482519565"
		}
	}))
	t.equal(error.data, "Channel ID 665310973967597573 is not currently bridged")

	db.prepare("INSERT INTO channel_room (channel_id, room_id, name, nick, thread_parent, custom_avatar, last_bridged_pin_timestamp, speedbump_id, speedbump_checked, speedbump_webhook_id, guild_id, custom_topic) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(row.channel_id, row.room_id, row.name, row.nick, row.thread_parent, row.custom_avatar, row.last_bridged_pin_timestamp, row.speedbump_id, row.speedbump_checked, row.speedbump_webhook_id, row.guild_id, row.custom_topic)
	const new_row = db.prepare("SELECT * FROM channel_room WHERE channel_id = '665310973967597573'").get()
	t.deepEqual(row, new_row)
})

// *****

test("web unlink space: access denied if not logged in to Discord", async t => {
	const [error] = await tryToCatch(() => router.test("post", "/api/unlink-space", {
		body: {
			guild_id: "665289423482519565"
		}
	}))
	t.equal(error.data, "Can't edit a guild you don't have Manage Server permissions in")
})

test("web unlink space: checks that guild exists", async t => {
	const [error] = await tryToCatch(() => router.test("post", "/api/unlink-space", {
		sessionData: {
			managedGuilds: ["2"]
		},
		body: {
			guild_id: "2"
		}
	}))
	t.equal(error.data, "Discord guild does not exist or bot has not joined it")
})

test("web unlink space: checks that a space is linked to the guild before trying to unlink the space", async t => {
	db.exec("BEGIN TRANSACTION")
	db.prepare("DELETE FROM guild_active WHERE guild_id = '665289423482519565'").run()

	const [error] = await tryToCatch(() => router.test("post", "/api/unlink-space", {
		sessionData: {
			managedGuilds: ["665289423482519565"]
		},
		body: {
			guild_id: "665289423482519565"
		}
	}))
	t.equal(error.data, "Discord guild has not been considered for bridging")

	db.exec("ROLLBACK") // ぬ
})

test("web unlink space: correctly abort unlinking if some linked channels remain after trying to unlink them all", async t => {
	let unbridgedChannel = false

	const [error] = await tryToCatch(() => router.test("post", "/api/unlink-space", {
		sessionData: {
			managedGuilds: ["665289423482519565"]
		},
		body: {
			guild_id: "665289423482519565",
		},
		createRoom: {
			async unbridgeChannel(channel, guildID) {
				unbridgedChannel = true
				t.ok(["1438284564815548418", "665310973967597573"].includes(channel.id))
				t.equal(guildID, "665289423482519565")
				// Do not actually delete the link from DB, should trigger error later in check
			}
		},
		api: {
			async *generateFullHierarchy(spaceID) {
				t.equal(spaceID, "!zTMspHVUBhFLLSdmnS:cadence.moe")
				yield {
					room_id: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
					children_state: [],
					guest_can_join: false,
					num_joined_members: 2
				}
				/* c8 ignore next */
			},
		}
	}))

	t.equal(error.data, "Failed to unlink some rooms. Please try doing it manually, or report a bug. The space will not be unlinked until all rooms are.")
	t.equal(unbridgedChannel, true)
})

test("web unlink space: successfully calls unbridgeChannel on linked channels in space, self-downgrade power level, leave space, and delete link from DB", async t => {
	const {reg} = require("../../matrix/read-registration")
	const me = `@${reg.sender_localpart}:${reg.ooye.server_name}`

	const getLinkRowQuery = "SELECT * FROM guild_space WHERE guild_id = '665289423482519565'"

	const row = db.prepare(getLinkRowQuery).get()
	t.equal(row.space_id, "!zTMspHVUBhFLLSdmnS:cadence.moe")

	let unbridgedChannel = false
	let downgradedPowerLevel = false
	let leftRoom = false
	await router.test("post", "/api/unlink-space", {
		sessionData: {
			managedGuilds: ["665289423482519565"]
		},
		body: {
			guild_id: "665289423482519565",
		},
		createRoom: {
			async unbridgeChannel(channel, guildID) {
				unbridgedChannel = true
				t.ok(["1438284564815548418", "665310973967597573"].includes(channel.id))
				t.equal(guildID, "665289423482519565")

				// In order to not simulate channel deletion and not trigger the post unlink channels, pre-unlink space check
				db.prepare("DELETE FROM channel_room WHERE channel_id = ?").run(channel.id)
			}
		},
		snow: {
			user: {
				// @ts-ignore - snowtransfer or discord-api-types broken, 204 No Content should be mapped to void but is actually mapped to never
				async leaveGuild(guildID) {
					t.equal(guildID, "665289423482519565")
				}
			}
		},
		api: {
			async *generateFullHierarchy(spaceID) {
				t.equal(spaceID, "!zTMspHVUBhFLLSdmnS:cadence.moe")
				yield {
					room_id: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
					children_state: [],
					guest_can_join: false,
					num_joined_members: 2
				}
				/* c8 ignore next */
			},

			async getStateEvent(roomID, type, key) { // getting power levels from space to apply to room
				t.equal(type, "m.room.power_levels")
				t.equal(key, "")
				return {users: {"@_ooye_bot:cadence.moe": 100, "@example:matrix.org": 50}, events: {"m.room.tombstone": 100}}
			},

			async getStateEventOuter(roomID, type, key) {
				t.equal(roomID, "!zTMspHVUBhFLLSdmnS:cadence.moe")
				t.equal(type, "m.room.create")
				t.equal(key, "")
				return {
					type: "m.room.create",
					state_key: "",
					sender: "@_ooye_bot:cadence.moe",
					room_id: "!zTMspHVUBhFLLSdmnS:cadence.moe",
					event_id: "$create",
					origin_server_ts: 0,
					content: {
						room_version: "11"
					}
				}
			},

			async sendState(roomID, type, key, content) {
				downgradedPowerLevel = true
				t.equal(roomID, "!zTMspHVUBhFLLSdmnS:cadence.moe")
				t.equal(type, "m.room.power_levels")
				t.notOk(me in content.users, `got ${JSON.stringify(content)} but expected bot user to not be present`)
				return ""
			},

			async leaveRoom(spaceID) {
				leftRoom = true
				t.equal(spaceID, "!zTMspHVUBhFLLSdmnS:cadence.moe")
			},
		}
	})

	t.equal(unbridgedChannel, true)
	t.equal(downgradedPowerLevel, true)
	t.equal(leftRoom, true)

	const missed_row = db.prepare(getLinkRowQuery).get()
	t.equal(missed_row, undefined)
})
