// <nowiki>
// Copied and edited from Novem Linguae's user highlighter simple: ]

class UserRoleIndicator {
	/**
	 * @param {jQuery} $ jquery
	 * @param {Object} mw mediawiki
	 * @param {Window} window
	 */
	constructor( $, mw, window ) {
		// eslint-disable-next-line no-jquery/variable-pattern
		this.$ = $;
		this.mw = mw;
		this.window = window;
        this.linksChecked = new Set();
	}    

	async execute() {
		//console.time("uri");
		const defaultRoleInfoLookup = {
			wmf: ,
			bot: ,
			stewards: ,
			arbcom: ,
			bureaucrats: ,
			checkUsers: ,
			admins: ,
			formerAdmins: ,
			newPageReviewers: ,
			tenThousandEdits: ,
			extendedConfirmed: ,
			lessThan500: ,
		};

		if(this.window.UserRoleIndicatorCustomLabels){
			this.roleInfoLookup = { ...defaultRoleInfoLookup, ...window.UserRoleIndicatorCustomLabels };
		}else{
			this.roleInfoLookup = defaultRoleInfoLookup;
		}

        this.labelPosition = "after";
        if(this.window.UserRoleIndicatorCustomPlacement){
            this.labelPosition = this.window.UserRoleIndicatorCustomPlacement;
        }

		//console.time("get usernames")
		await this.getUsernames();
		//console.timeEnd("get usernames")
		
		this.addCSS('user-role-indicator', 'font-size: smaller; display: inline; background: #b7b9ff55; padding: 0.1em; border-radius: 5px;')
		this.addCSS('label-after', 'margin-left:3px;');
		this.addCSS('label-before', 'margin-right:1px;margin-left:2px;');

		const $links = this.$( '#article a, #bodyContent a, #mw_contentholder a' );

		//console.time("linkloop")
		$links.each( ( index, element ) => {
			this.$link = this.$( element );
            if(this.linksChecked.has(element)){
                return;
            }

            this.linksChecked.add(element);

			if ( !this.linksToAUser() ) {
				return;
			}
			this.user = this.getUserName();
			const isUserSubpage = this.user.includes( '/' );
			if ( isUserSubpage ) {
				return;
			}
			this.hasAdvancedPermissions = false;
			this.addRoleInfoIfNeeded();
		} );
		//console.timeEnd("linkloop")

		//console.timeEnd("uri");
        //console.log("-------");
	}

	addCSS( htmlClass, cssDeclaration ) {
		// .plainlinks is for Wikipedia Signpost articles
		// To support additional custom signature edge cases, add to the selectors here.
		this.mw.util.addCSS( `
			.plainlinks .${ htmlClass }.external,
			.${ htmlClass },
			.${ htmlClass } b,
			.${ htmlClass } big,
			.${ htmlClass } font,
			.${ htmlClass } kbd,
			.${ htmlClass } small,
			.${ htmlClass } span {
				${ cssDeclaration }
			}
		` );
	}

	async getWikitextFromCache( title ) {
		const api = new this.mw.ForeignApi( 'https://en.wikipedia.org/w/api.php' );
		let wikitext = '';
		await api.get( {
			action: 'query',
			prop: 'revisions',
			titles: title,
			rvslots: '*',
			rvprop: 'content',
			formatversion: '2',
			uselang: 'content', // needed for caching
			smaxage: '86400', // cache for 1 day
			maxage: '86400' // cache for 1 day
		} ).then( ( data ) => {
			wikitext = data.query.pages.revisions.slots.main.content;
		} );
		return wikitext;
	}

	async getUsernames() {
        
        if(this.wmf){
            return;
        }

		const dataString = await this.getWikitextFromCache( 'User:NovemBot/userlist.js' );
		const dataJSON = JSON.parse( dataString );

		this.wmf = {
			...dataJSON.founder,
			...dataJSON.boardOfTrustees,
			...dataJSON.staff
			// WMF is hard-coded a bit further down. The script detects those strings in the username. This is safe to do because the WMF string is blacklisted from names, so has to be specially created.
			// ...dataJSON,
			// ...dataJSON,
			// ...dataJSON,
			// ...dataJSON,
			// ...dataJSON,
		};
		this.bot = dataJSON.bot;
		this.stewards = dataJSON.steward;
		this.arbcom = dataJSON.arbcom;
		this.bureaucrats = dataJSON.bureaucrat;
		this.admins = dataJSON.sysop;
		this.checkUsers = dataJSON.checkuser;
		this.formerAdmins = dataJSON.formeradmin;
		this.newPageReviewers = dataJSON.patroller;
		this.tenThousandEdits = dataJSON;
		this.extendedConfirmed = {
			...dataJSON.extendedconfirmed,
			...dataJSON.productiveIPs
		};
	}

	hasHref( url ) {
		return Boolean( url );
	}

	isAnchor( url ) {
		return url.charAt( 0 ) === '#';
	}

	isHttpOrHttps( url ) {
		return url.startsWith( 'http://', 0 ) ||
			url.startsWith( 'https://', 0 ) ||
			url.startsWith( '/', 0 );
	}

	/**
	 * Figure out the wikipedia article title of the link
	 *
	 * @param {string} url
	 * @param {mw.Uri} urlHelper
	 * @return {string}
	 */
	getTitle( url, urlHelper ) {
		// for links in the format /w/index.php?title=Blah
		const titleParameterOfUrl = this.mw.util.getParamValue( 'title', url );
		if ( titleParameterOfUrl ) {
			return titleParameterOfUrl;
		}

		// for links in the format https://wikines.com/en/PageName. Slice off the https://wikines.com/en/ (first 6 characters)
		if ( urlHelper.path.startsWith( 'https://wikines.com/en/' ) ) {
			return decodeURIComponent( urlHelper.path.slice( 6 ) );
		}

		return '';
	}

	notInUserOrUserTalkNamespace() {
		const namespace = this.titleHelper.getNamespaceId();
		const notInSpecialUserOrUserTalkNamespace = this.$.inArray( namespace,  ) === -1;
		return notInSpecialUserOrUserTalkNamespace;
	}

	linksToAUser() {
		let url = this.$link.attr( 'href' );

		if ( !this.hasHref( url ) || this.isAnchor( url ) || !this.isHttpOrHttps( url ) ) {
			return false;
		}

		url = this.addDomainIfMissing( url );

		// mw.Uri(url) throws an error if it doesn't like the URL. An example of a URL it doesn't like is https://meta.wikimedia.orghttps://wikines.com/en/Community_Wishlist_Survey_2022/Larger_suggestions#1%, which has a section link to a section titled 1% (one percent).
		let urlHelper;
		try {
			urlHelper = new this.mw.Uri( url );
		} catch {
			return false;
		}

		// Skip links that aren't to user pages
		const isUserPageLink = url.includes( '/w/index.php?title=User' ) || url.includes( 'https://wikines.com/en/User' );
		if ( !isUserPageLink ) {
			return false;
		}

		// Even if it is a link to a userpage, skip URLs that have any parameters except title=User, action=edit, and redlink=. We don't want links to diff pages, section editing pages, etc. to be highlighted.
		const urlParameters = urlHelper.query;
		delete urlParameters.title;
		delete urlParameters.action;
		delete urlParameters.redlink;
		const hasNonUserpageParametersInUrl = !this.$.isEmptyObject( urlParameters );
		if ( hasNonUserpageParametersInUrl ) {
			return false;
		}

		const title = this.getTitle( url, urlHelper );

		// Handle edge cases such as https://web.archive.org/web/20231105033559/https://en.wikipedia.orghttps://wikines.com/en/User:SandyGeorgia/SampleIssue, which shows up as isUserPageLink = true but isn't really a user page.
		try {
			this.titleHelper = new this.mw.Title( title );
		} catch {
			return false;
		}

		if ( this.notInUserOrUserTalkNamespace() ) {
			return false;
		}

		const isDiscussionToolsSectionLink = url.includes( '#' );
		if ( isDiscussionToolsSectionLink ) {
			return false;
		}

		return true;
	}

	// Brandon Frohbieter, CC BY-SA 4.0, https://stackoverflow.com/a/4009771/3480193
	countInstances( string, word ) {
		return string.split( word ).length - 1;
	}

	/**
	 * mw.Uri(url) expects a complete URL. If we get something like https://wikines.com/en/User:Test, convert it to https://en.wikipedia.orghttps://wikines.com/en/User:Test. Without this, UserHighlighterSimple doesn't work on metawiki.
	 *
	 * @param {string} url
	 * @return {string} url
	 */
	addDomainIfMissing( url ) {
		if ( url.startsWith( '/' ) ) {
			url = window.location.origin + url;
		}
		return url;
	}

	/**
	 * @return {string}
	 */
	getUserName() {
		const user = this.titleHelper.getMain().replace( /_/g, ' ' );
		return user;
	}

	addRoleInfoIfAppropriate( listOfUsernames, label, descriptionForHover ) {
		if ( listOfUsernames === 1 ) {
			this.addRoleIcon( label, descriptionForHover );
		}
	}

	addRoleIcon( icon, descriptionForHover ) {

		const title = this.$link.attr( 'title' );
		if ( !title || title.startsWith( 'User:' ) ) {
			this.$link.attr( 'title', descriptionForHover );

            switch(this.labelPosition){
                case "before":
                    this.$link.prepend($("<div class='user-role-indicator label-before'>"+icon+"</div>"))
                break;

                default:
                    // Defaults to "after"
                    this.$link.append($("<div class='user-role-indicator label-after'>"+icon+"</div>"))
                    break;
            }
		}

		this.hasAdvancedPermissions = true;
	}

	addRoleInfoIfNeeded() {
		
		// highlight anybody with "WMF" in their name, case insensitive. this should not generate false positives because "WMF" is on the username blacklist. see https://meta.wikimedia.orghttps://wikines.com/en/Title_blacklist
		if ( this.user.match( /^*WMF/i ) ) {
			this.addRoleIcon( this.roleInfoLookup.wmf, this.roleInfoLookup.wmf );
		}

		// TODO: grab the order from an array, so I can keep checkForPermission and addCSS in the same order easily, lowering the risk of the HTML title="" being one thing, and the color being another
		this.addRoleInfoIfAppropriate( this.wmf, this.roleInfoLookup.wmf, this.roleInfoLookup.wmf);
		this.addRoleInfoIfAppropriate( this.bot, this.roleInfoLookup.bot, this.roleInfoLookup.bot);
		this.addRoleInfoIfAppropriate( this.stewards, this.roleInfoLookup.stewards, this.roleInfoLookup.stewards);
		this.addRoleInfoIfAppropriate( this.arbcom, this.roleInfoLookup.arbcom, this.roleInfoLookup.arbcom);
		this.addRoleInfoIfAppropriate( this.bureaucrats, this.roleInfoLookup.bureaucrats, this.roleInfoLookup.bureaucrats);
		this.addRoleInfoIfAppropriate( this.checkUsers, this.roleInfoLookup.checkUsers, this.roleInfoLookup.checkUsers);
		this.addRoleInfoIfAppropriate( this.admins, this.roleInfoLookup.admins, this.roleInfoLookup.admins);
		this.addRoleInfoIfAppropriate( this.formerAdmins, this.roleInfoLookup.formerAdmins, this.roleInfoLookup.formerAdmins);
		this.addRoleInfoIfAppropriate( this.newPageReviewers, this.roleInfoLookup.newPageReviewers, this.roleInfoLookup.newPageReviewers);
		this.addRoleInfoIfAppropriate( this.tenThousandEdits, this.roleInfoLookup.tenThousandEdits, this.roleInfoLookup.tenThousandEdits);
		this.addRoleInfoIfAppropriate( this.extendedConfirmed, this.roleInfoLookup.extendedConfirmed, this.roleInfoLookup.extendedConfirmed);

		// If they have no perms, then they are non-EC, so <500 edits
		if ( !this.hasAdvancedPermissions ) {
			this.addRoleIcon(this.roleInfoLookup.lessThan500, this.roleInfoLookup.lessThan500);
		}
	}
}

var userRoleIndicator = new UserRoleIndicator( $, mw, window )

// Fire after wiki content is added to the DOM, such as when first loading a page, or when a gadget such as the XTools gadget loads.
mw.hook( 'wikipage.content' ).add( async () => {
	await mw.loader.using( , async () => {
		await userRoleIndicator.execute();
	} );
} );

// Fire after an edit is successfully saved via JavaScript, such as edits by the Visual Editor and HotCat.
mw.hook( 'postEdit' ).add( async () => {
	await mw.loader.using( , async () => {
		await userRoleIndicator.execute();
	} );
} );

// </nowiki>