').text(dashboardLang.get('settings.new'))
			).attr('title', dashboardLang.get('settings.new')).attr('href', `/guild/${guild.id}/settings/new${suffix}`) )
		);
		if ( args[4] === 'new' && !process.env.READONLY ) {
			$('.channel#channel-new').addClass('selected');
			createForm($, dashboardLang.get('settings.form.new'), dashboardLang, Object.assign({}, rows.find( row => !row.channel ), {
				patreon: isPatreon,
				channel: 'new'
			}), guild.roles, guild.channels.filter( channel => {
				return ( channel.isCategory || !rows.some( row => row.channel === ( channel.isCategory ? '#' : '' ) + channel.id ) );
			} ).map( channel => {
				if ( !channel.isCategory ) return channel;
				let {id, name, userPermissions, isCategory} = channel;
				return {
					id, name, userPermissions, isCategory,
					allowedCat: !rows.some( row => row.channel === '#' + channel.id )
				};
			} )).attr('action', `/guild/${guild.id}/settings/new`).appendTo('#text');
		}
		else if ( channellist.some( channel => channel.id === args[4] ) ) {
			let channel = channellist.find( channel => channel.id === args[4] );
			$(`.channel#channel-${channel.id}`).addClass('selected');
			createForm($, dashboardLang.get('settings.form.overwrite', false, `#${channel.name}`), dashboardLang, Object.assign({}, rows.find( row => {
				return row.channel === ( channel.isCategory ? '#' : '' ) + channel.id;
			} ), {
				patreon: isPatreon
			}), guild.roles, [channel]).attr('action', `/guild/${guild.id}/settings/${channel.id}`).appendTo('#text');
		}
		else {
			$('.channel#settings').addClass('selected');
			createForm($, dashboardLang.get('settings.form.default'), dashboardLang, rows.find( row => !row.channel ), guild.roles).attr('action', `/guild/${guild.id}/settings/default`).appendTo('#text');
		}
	}, dberror => {
		console.log( '- Dashboard: Error while getting the settings: ' + dberror );
		createNotice($, 'error', dashboardLang);
		$('
').text(dashboardLang.get('settings.failed')).appendTo('#text .description');
		$('.channel#settings').addClass('selected');
	} ).then( () => {
		let body = $.html();
		res.writeHead(200, {'Content-Length': Buffer.byteLength(body)});
		res.write( body );
		return res.end();
	} );
}
/**
 * Change settings
 * @param {Function} res - The server response
 * @param {import('./util.js').Settings} userSettings - The settings of the user
 * @param {String} guild - The id of the guild
 * @param {String} type - The setting to change
 * @param {Object} settings - The new settings
 * @param {String} [settings.channel]
 * @param {String} settings.wiki
 * @param {String} [settings.lang]
 * @param {String} [settings.role]
 * @param {String} [settings.inline]
 * @param {String} [settings.prefix]
 * @param {String} [settings.prefix_space]
 * @param {String} [settings.save_settings]
 * @param {String} [settings.delete_settings]
 */
function update_settings(res, userSettings, guild, type, settings) {
	if ( type !== 'default' && type !== 'new' && type !== settings.channel ) {
		return res(`/guild/${guild}/settings`, 'savefail');
	}
	if ( !settings.save_settings === !settings.delete_settings ) {
		return res(`/guild/${guild}/settings/${type}`, 'savefail');
	}
	if ( settings.save_settings ) {
		if ( !settings.wiki || ( settings.lang && !allLangs.hasOwnProperty(settings.lang) ) ) {
			return res(`/guild/${guild}/settings/${type}`, 'savefail');
		}
		if ( settings.channel && !userSettings.guilds.isMember.get(guild).channels.some( channel => {
			return ( channel.id === settings.channel && ( !channel.isCategory || userSettings.guilds.isMember.get(guild).patreon ) );
		} ) ) return res(`/guild/${guild}/settings/${type}`, 'savefail');
		if ( settings.role && !userSettings.guilds.isMember.get(guild).roles.some( role => {
			return ( role.id === settings.role );
		} ) ) return res(`/guild/${guild}/settings/${type}`, 'savefail');
	}
	if ( settings.delete_settings && ( type === 'default' || type === 'new' ) ) {
		return res(`/guild/${guild}/settings/${type}`, 'savefail');
	}
	sendMsg( {
		type: 'getMember',
		member: userSettings.user.id,
		guild: guild,
		channel: ( type !== 'default' ? settings.channel : undefined ),
		allowCategory: true
	} ).then( response => {
		if ( !response ) {
			userSettings.guilds.notMember.set(guild, userSettings.guilds.isMember.get(guild));
			userSettings.guilds.isMember.delete(guild);
			return res(`/guild/${guild}`, 'savefail');
		}
		if ( response === 'noMember' || !hasPerm(response.userPermissions, 'MANAGE_GUILD') ) {
			userSettings.guilds.isMember.delete(guild);
			return res('/', 'savefail');
		}
		if ( response.message === 'noChannel' ) return db.query( 'DELETE FROM discord WHERE guild = $1 AND ( channel = $2 OR channel = $3 )', [guild, type, `#${type}`] ).then( () => {
			console.log( `- Dashboard: Settings successfully removed: ${guild}#${type}` );
			if ( settings.delete_settings ) return res(`/guild/${guild}/settings`, 'save');
			else return res(`/guild/${guild}/settings`, 'savefail');
		}, delerror =>{
			console.log( '- Dashboard: Error while removing the settings: ' + delerror );
			return res(`/guild/${guild}/settings`, 'savefail');
		} );
		if ( type !== 'default' && !hasPerm(response.userPermissions, 'VIEW_CHANNEL', 'SEND_MESSAGES') ) {
			return res(`/guild/${guild}/settings/${type}`, 'savefail');
		}
		if ( settings.delete_settings ) return db.query( 'DELETE FROM discord WHERE guild = $1 AND channel = $2 RETURNING wiki, lang, role, inline', [guild, ( response.isCategory ? '#' : '' ) + type] ).then( ({rows:[channel]}) => {
			console.log( `- Dashboard: Settings successfully removed: ${guild}#${type}` );
			res(`/guild/${guild}/settings`, 'save');
			if ( !channel ) return;
			db.query( 'SELECT channel, wiki, lang, role, inline FROM discord WHERE guild = $1 AND ( channel = $2 OR channel IS NULL ) ORDER BY channel DESC NULLS LAST', [guild, '#' + response.parentId] ).then( ({rows:[row, {lang: guildlang} = {}]}) => {
				var lang = new Lang(( guildlang || row.lang ));
				var text = lang.get('settings.dashboard.removed', `<@${userSettings.user.id}>`, `<#${type}>`);
				if ( channel.wiki !== row.wiki ) text += `\n${lang.get('settings.currentwiki')} <${channel.wiki}>`;
				if ( response.patreon ) {
					if ( channel.lang !== row.lang ) text += `\n${lang.get('settings.currentlang')} \`${allLangs[channel.lang]}\``;
					if ( channel.role !== row.role ) text += `\n${lang.get('settings.currentrole')} ` + ( channel.role ? `<@&${channel.role}>` : '@everyone' );
					if ( channel.inline !== row.inline ) text += `\n${lang.get('settings.currentinline')} ${( channel.inline ? '~~' : '' )}\`[[${( lang.localNames.page || 'page' )}]]\`${( channel.inline ? '~~' : '' )}`;
				}
				text += `\n<${new URL(`/guild/${guild}/settings`, process.env.dashboard).href}>`;
				sendMsg( {
					type: 'notifyGuild', guild, text
				} ).catch( error => {
					console.log( '- Dashboard: Error while notifying the guild: ' + error );
				} );
			}, dberror => {
				console.log( '- Dashboard: Error while notifying the guild: ' + dberror );
			} );
		}, dberror => {
			console.log( '- Dashboard: Error while removing the settings: ' + dberror );
			return res(`/guild/${guild}/settings/${type}`, 'savefail');
		} );
		var wiki = Wiki.fromInput(settings.wiki);
		if ( !wiki ) return res(`/guild/${guild}/settings`, 'savefail');
		var embed;
		return got.get( wiki + 'api.php?&action=query&meta=siteinfo&siprop=general&format=json', {
			responseType: 'text'
		} ).then( fresponse => {
			try {
				fresponse.body = JSON.parse(fresponse.body);
			}
			catch (error) {
				if ( fresponse.statusCode === 404 && typeof fresponse.body === 'string' ) {
					let api = cheerioLoad(fresponse.body)('head link[rel="EditURI"]').prop('href');
					if ( api ) {
						wiki = new Wiki(api.split('api.php?')[0], wiki);
						return got.get( wiki + 'api.php?action=query&meta=siteinfo&siprop=general&format=json' );
					}
				}
			}
			return fresponse;
		} ).then( fresponse => {
			return db.query( 'SELECT channel, wiki, lang, role, inline, prefix FROM discord WHERE guild = $1 AND ( channel = $2 OR channel IS NULL ) ORDER BY channel DESC NULLS LAST', [guild, '#' + response.parentId] ).then( ({rows:[row, {lang: guildlang} = {}]}) => {
				if ( row ) row.guildlang = ( guildlang || row.lang );
				var body = fresponse.body;
				if ( fresponse.statusCode !== 200 || body?.batchcomplete === undefined || !body?.query?.general ) {
					console.log( '- Dashboard: ' + fresponse.statusCode + ': Error while testing the wiki: ' + body?.error?.info );
					if ( row?.wiki === wiki.href ) return row;
					if ( body?.error?.info === 'You need read permission to use this module.' ) {
						return Promise.reject('private');
					}
					return Promise.reject();
				}
				wiki.updateWiki(body.query.general);
				if ( !wiki.isFandom() ) {
					let lang = new Lang(( type === 'default' && settings.lang || row?.guildlang ));
					let notice = [];
					if ( body.query.general.generator.replace( /^MediaWiki 1\.(\d\d).*$/, '$1' ) <= 30 ) {
						console.log( '- Dashboard: This wiki is using ' + body.query.general.generator + '.' );
						notice.push({
							name: 'MediaWiki',
							value: lang.get('test.MediaWiki', '[MediaWiki 1.30](https://www.mediawiki.org/wiki/MediaWiki_1.30)', body.query.general.generator)
						});
					}
					if ( notice.length ) {
						embed = {
							author: {name: body.query.general.sitename},
							title: lang.get('test.notice'),
							fields: notice
						}
					}
				}
				return row;
			}, dberror => {
				console.log( '- Dashboard: Error while getting the settings: ' + dberror );
				return Promise.reject();
			} );
		}, error => {
			if ( error.message?.startsWith( 'connect ECONNREFUSED ' ) || error.message?.startsWith( 'Hostname/IP does not match certificate\'s altnames: ' ) || error.message === 'certificate has expired' || error.message === 'self signed certificate' ) {
				console.log( '- Dashboard: Error while testing the wiki: No HTTPS' );
				return Promise.reject('http');
			}
			console.log( '- Dashboard: Error while testing the wiki: ' + error );
			if ( error.message === `Timeout awaiting 'request' for ${got.defaults.options.timeout.request}ms` ) {
				return Promise.reject('timeout');
			}
			return Promise.reject();
		} ).then( row => {
			var lang = new Lang(( type === 'default' && settings.lang || row?.guildlang ));
			if ( type === 'default' ) {
				if ( settings.channel || !settings.lang || ( !response.patreon !== !settings.prefix ) ) {
					return res(`/guild/${guild}/settings`, 'savefail');
				}
				if ( settings.prefix ) {
					if ( !/^\s*[^\s`\\]{1,100}\s*$/.test(settings.prefix) ) {
						return res(`/guild/${guild}/settings`, 'savefail');
					}
					settings.prefix = settings.prefix.trim().toLowerCase();
					if ( settings.prefix_space ) settings.prefix += ' ';
				}
				if ( !row ) return db.query( 'INSERT INTO discord(wiki, lang, role, inline, prefix, guild, main) VALUES($1, $2, $3, $4, $5, $6, $6)', [wiki.href, settings.lang, ( settings.role || null ), ( settings.inline ? null : 1 ), ( settings.prefix || process.env.prefix ), guild] ).then( () => {
					console.log( '- Dashboard: Settings successfully saved: ' + guild );
					res(`/guild/${guild}/settings`, 'save');
					var text = lang.get('settings.dashboard.updated', `<@${userSettings.user.id}>`);
					text += '\n' + lang.get('settings.currentwiki') + ` <${wiki.href}>`;
					text += '\n' + lang.get('settings.currentlang') + ` \`${allLangs[settings.lang]}\``;
					text += '\n' + lang.get('settings.currentrole') + ( settings.role ? ` <@&${settings.role}>` : ' @everyone' );
					if ( response.patreon ) {
						text += '\n' + lang.get('settings.currentprefix') + ` \`${settings.prefix.replace( /\\/g, '\\$&' )}\``;
					}
					text += '\n' + lang.get('settings.currentinline') + ` ${( settings.inline ? '' : '~~' )}\`[[${( lang.localNames.page || 'page' )}]]\`${( settings.inline ? '' : '~~' )}`;
					text += `\n<${new URL(`/guild/${guild}/settings`, process.env.dashboard).href}>`;
					sendMsg( {
						type: 'notifyGuild', guild, text, embed,
						file: [`./i18n/widgets/${settings.lang}.png`]
					} ).catch( error => {
						console.log( '- Dashboard: Error while notifying the guild: ' + error );
					} );
				}, dberror => {
					console.log( '- Dashboard: Error while saving the settings: ' + dberror );
					return res(`/guild/${guild}/settings`, 'savefail');
				} );
				var diff = [];
				var file = [];
				var updateGuild = false;
				var updateChannel = false;
				if ( row.wiki !== wiki.href ) {
					updateGuild = true;
					diff.push(lang.get('settings.currentwiki') + ` ~~<${row.wiki}>~~ → <${wiki.href}>`);
				}
				if ( row.lang !== settings.lang ) {
					updateChannel = true;
					file.push(`./i18n/widgets/${settings.lang}.png`);
					diff.push(lang.get('settings.currentlang') + ` ~~\`${allLangs[row.lang]}\`~~ → \`${allLangs[settings.lang]}\``);
				}
				if ( response.patreon && row.prefix !== settings.prefix ) {
					updateChannel = true;
					diff.push(lang.get('settings.currentprefix') + ` ~~\`${row.prefix.replace( /\\/g, '\\$&' )}\`~~ → \`${settings.prefix.replace( /\\/g, '\\$&' )}\``);
				}
				if ( row.role !== ( settings.role || null ) ) {
					updateChannel = true;
					diff.push(lang.get('settings.currentrole') + ` ~~${( row.role ? `<@&${row.role}>` : '@everyone' )}~~ → ${( settings.role ? `<@&${settings.role}>` : '@everyone' )}`);
				}
				if ( row.inline !== ( settings.inline ? null : 1 ) ) {
					updateChannel = true;
					let inlinepage = ( lang.localNames.page || 'page' );
					diff.push(lang.get('settings.currentinline') + ` ${( row.inline ? '~~' : '' )}\`[[${inlinepage}]]\`${( row.inline ? '~~' : '' )} → ${( settings.inline ? '' : '~~' )}\`[[${inlinepage}]]\`${( settings.inline ? '' : '~~' )}`);
				}
				if ( diff.length ) {
					var dbupdate = [];
					if ( response.patreon ) {
						dbupdate.push([
							'UPDATE discord SET wiki = $1, lang = $2, role = $3, inline = $4, prefix = $5 WHERE guild = $6 AND channel IS NULL',
							[wiki.href, settings.lang, ( settings.role || null ), ( settings.inline ? null : 1 ), ( settings.prefix || process.env.prefix ), guild]
						]);
					}
					else {
						if ( updateGuild ) {
							dbupdate.push([
								'UPDATE discord SET wiki = $1 WHERE guild = $2 AND channel IS NULL',
								[wiki.href, guild]
							]);
						}
						if ( updateChannel ) {
							dbupdate.push([
								'UPDATE discord SET lang = $1, role =  $2, inline =  $3, prefix = $4 WHERE guild = $5',
								[settings.lang, ( settings.role || null ), ( settings.inline ? null : 1 ), ( settings.prefix || process.env.prefix ), guild]
							]);
						}
					}
					return Promise.all(dbupdate.map( ([sql, sqlargs]) => {
						return db.query( sql, sqlargs );
					} )).then( () => {
						console.log( '- Dashboard: Settings successfully saved: ' + guild );
						res(`/guild/${guild}/settings`, 'save');
						var text = lang.get('settings.dashboard.updated', `<@${userSettings.user.id}>`);
						text += '\n' + diff.join('\n');
						text += `\n<${new URL(`/guild/${guild}/settings`, process.env.dashboard).href}>`;
						sendMsg( {
							type: 'notifyGuild', guild, text, file,
							embed: ( updateGuild ? embed : undefined ),
							prefix: settings.prefix, voice: settings.lang
						} ).catch( error => {
							console.log( '- Dashboard: Error while notifying the guild: ' + error );
						} );
					}, dberror => {
						console.log( '- Dashboard: Error while saving the settings: ' + dberror );
						return res(`/guild/${guild}/settings`, 'savefail');
					} );
				}
				return res(`/guild/${guild}/settings`, 'save');
			}
			if ( !row || !settings.channel || settings.prefix || 
			( !response.patreon && ( settings.lang || settings.role || settings.inline ) ) ) {
				return res(`/guild/${guild}/settings`, 'savefail');
			}
			if ( row.wiki === wiki.href && ( !response.patreon || 
			( row.lang === settings.lang && row.inline === ( settings.inline ? null : 1 ) && row.role === ( settings.role || null ) ) ) ) {
				if ( type === 'new' ) {
					return res(`/guild/${guild}/settings/${type}`, 'nochange');
				}
				return db.query( 'DELETE FROM discord WHERE guild = $1 AND channel = $2 RETURNING wiki, lang, role, inline', [guild, ( response.isCategory ? '#' : '' ) + type] ).then( ({rows:[channel]}) => {
					console.log( `- Dashboard: Settings successfully removed: ${guild}#${type}` );
					res(`/guild/${guild}/settings`, 'save');
					var text = lang.get('settings.dashboard.removed', `<@${userSettings.user.id}>`, `<#${type}>`);
					if ( channel.wiki !== row.wiki ) text += `\n${lang.get('settings.currentwiki')} <${channel.wiki}>`;
					if ( response.patreon ) {
						if ( channel.lang !== row.lang ) text += `\n${lang.get('settings.currentlang')} \`${allLangs[channel.lang]}\``;
						if ( channel.role !== row.role ) text += `\n${lang.get('settings.currentrole')} ` + ( channel.role ? `<@&${channel.role}>` : '@everyone' );
						if ( channel.inline !== row.inline ) text += `\n${lang.get('settings.currentinline')} ${( channel.inline ? '~~' : '' )}\`[[${( lang.localNames.page || 'page' )}]]\`${( channel.inline ? '~~' : '' )}`;
					}
					text += `\n<${new URL(`/guild/${guild}/settings`, process.env.dashboard).href}>`;
					sendMsg( {
						type: 'notifyGuild', guild, text
					} ).catch( error => {
						console.log( '- Dashboard: Error while notifying the guild: ' + error );
					} );
				}, dberror => {
					console.log( '- Dashboard: Error while removing the settings: ' + dberror );
					return res(`/guild/${guild}/settings/${type}`, 'savefail');
				} );
			}
			return db.query( 'SELECT lang, wiki, role, inline FROM discord WHERE guild = $1 AND channel = $2', [guild, ( response.isCategory ? '#' : '' ) + settings.channel] ).then( ({rows:[channel]}) => {
				if ( !channel ) channel = row;
				var diff = [];
				var file = [];
				var useEmbed = false;
				if ( channel.wiki !== wiki.href ) {
					useEmbed = true;
					diff.push(lang.get('settings.currentwiki') + ` ~~<${channel.wiki}>~~ → <${wiki.href}>`);
				}
				if ( response.patreon && channel.lang !== settings.lang ) {
					file.push(`./i18n/widgets/${settings.lang}.png`);
					diff.push(lang.get('settings.currentlang') + ` ~~\`${allLangs[channel.lang]}\`~~ → \`${allLangs[settings.lang]}\``);
				}
				if ( response.patreon && channel.role !== ( settings.role || null ) ) {
					diff.push(lang.get('settings.currentrole') + ` ~~${( channel.role ? `<@&${channel.role}>` : '@everyone' )}~~ → ${( settings.role ? `<@&${settings.role}>` : '@everyone' )}`);
				}
				if ( response.patreon && channel.inline !== ( settings.inline ? null : 1 ) ) {
					let inlinepage = ( lang.localNames.page || 'page' );
					diff.push(lang.get('settings.currentinline') + ` ${( channel.inline ? '~~' : '' )}\`[[${inlinepage}]]\`${( channel.inline ? '~~' : '' )} → ${( settings.inline ? '' : '~~' )}\`[[${inlinepage}]]\`${( settings.inline ? '' : '~~' )}`);
				}
				if ( !diff.length ) {
					return res(`/guild/${guild}/settings/${settings.channel}`, 'save');
				}
				let sql = 'UPDATE discord SET wiki = $1, lang = $2, role = $3, inline = $4 WHERE guild = $5 AND channel = $6';
				let sqlargs = [wiki.href, ( settings.lang || channel.lang ), ( response.patreon ? ( settings.role || null ) : channel.role ), ( response.patreon ? ( settings.inline ? null : 1 ) : channel.inline ), guild, ( response.isCategory ? '#' : '' ) + settings.channel];
				if ( channel === row ) {
					sql = 'INSERT INTO discord(wiki, lang, role, inline, guild, channel, prefix) VALUES($1, $2, $3, $4, $5, $6, $7)';
					sqlargs.push(row.prefix);
				}
				return db.query( sql, sqlargs ).then( () => {
					console.log( `- Dashboard: Settings successfully saved: ${guild}#${settings.channel}` );
					res(`/guild/${guild}/settings/${settings.channel}`, 'save');
					var text = lang.get('settings.dashboard.channel', `<@${userSettings.user.id}>`, `<#${settings.channel}>`);
					text += '\n' + diff.join('\n');
					text += `\n<${new URL(`/guild/${guild}/settings/${settings.channel}`, process.env.dashboard).href}>`;
					sendMsg( {
						type: 'notifyGuild', guild, text, file,
						embed: ( useEmbed ? embed : undefined )
					} ).catch( error => {
						console.log( '- Dashboard: Error while notifying the guild: ' + error );
					} );
				}, dberror => {
					console.log( '- Dashboard: Error while saving the settings: ' + dberror );
					return res(`/guild/${guild}/settings/${type}`, 'savefail');
				} );
			}, dberror => {
				console.log( '- Dashboard: Error while getting the channel settings: ' + dberror );
				return res(`/guild/${guild}/settings/${type}`, 'savefail');
			} );
		}, error => {
			return res(`/guild/${guild}/settings/${type}`, 'savefail', error);
		} );
	}, error => {
		console.log( '- Dashboard: Error while getting the member: ' + error );
		return res(`/guild/${guild}/settings/${type}`, 'savefail');
	} );
}
export {
	dashboard_settings as get,
	update_settings as post
};