Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
/**
 * RfX Starter User Script for Wikipedia 
 * Helps users submit RfA/RfB nominations by removing substitution-blocking comments and adding transclusions.
 * Version 1.0.0
 */
(function() {
    'use strict';

    // --- Configuration ---
    const config = {
        pageName: mw.config.get('wgPageName'),
        userName: mw.config.get('wgUserName') || 'YourUsername',
        tagLine: ' (using [[User:Barkeep49/rfaStarter.js|rfaStarter]])',
        apiDefaults: {
            format: 'json',
            formatversion: 2
        }
    };

    // Determine RfX type and base page name
    config.rfxType = config.pageName.includes('Requests_for_adminship') ? 'adminship' : 'bureaucratship';
    config.baseRfxPage = `Wikipedia:Requests_for_${config.rfxType}`;

    // Ensure the script only runs on valid RfX subpages (e.g., WP:RfA/Candidate, not WP:RfA)
    if (!new RegExp(`^${config.baseRfxPage.replace(/ /g, '_')}/[^/]+$`).test(config.pageName)) {
        return; // Exit if not on a valid RfX subpage
    }

    // --- State Variables ---
    let basePageWikitextCache = {};

    // --- Utility Functions ---

    /** Escapes characters for use in regex, handling spaces/underscores. */
    function escapeRegex(string) {
        const spacedString = string.replace(/_/g, ' ');
        const underscoredString = string.replace(/ /g, '_');
        if (spacedString === underscoredString) {
            return spacedString.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
        }
        const escapedSpaced = spacedString.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
        const escapedUnderscored = underscoredString.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
        return `(?:${escapedUnderscored}|${escapedSpaced})`;
    }

    /** Generic API request helper. */
    async function makeApiRequest(params, method = 'get', tokenType = null) {
        const api = new mw.Api();
        const fullParams = { ...config.apiDefaults, ...params };
        try {
            let response;
            if (method === 'get') {
                response = await api.get(fullParams);
            } else if (method === 'post' && tokenType) {
                response = await api.postWithToken(tokenType, fullParams);
            } else if (method === 'post') {
                response = await api.post(fullParams);
            } else {
                throw new Error(`Unsupported API method: ${method}`);
            }
            return response;
        } catch (error) {
            const errorCode = error?.error?.code || error?.textStatus || 'unknown';
            const errorInfo = error?.error?.info || error?.xhr?.responseText || 'Unknown API error';
            console.error(`RfA Starter API [${method.toUpperCase()} ${params.action}] Error:`, { params, errorCode, errorInfo, errorObj: error });
            throw { code: errorCode, info: errorInfo, params: params };
        }
    }

    /** Fetches the full wikitext of a given page, using cache. */
    async function fetchPageWikitext(targetPageName) {
        const normalizedPageName = targetPageName.replace(/ /g, '_');
        if (basePageWikitextCache[normalizedPageName]) {
            return basePageWikitextCache[normalizedPageName];
        }

        try {
            const data = await makeApiRequest({
                action: 'query',
                prop: 'revisions',
                titles: normalizedPageName,
                rvslots: 'main',
                rvprop: 'content'
            });
            const page = data.query.pages[0];
            if (page && page.missing) {
                basePageWikitextCache[normalizedPageName] = '';
                return '';
            }
            if (page && page.revisions?.[0]?.slots?.main?.content) {
                const wikitext = page.revisions[0].slots.main.content;
                basePageWikitextCache[normalizedPageName] = wikitext;
                return wikitext;
            } else {
                throw new Error(`Could not find wikitext content for page ${normalizedPageName}. Response: ${JSON.stringify(data)}`);
            }
        } catch (error) {
            console.error(`RfA Starter: Error fetching wikitext for ${normalizedPageName}:`, error);
            return null;
        }
    }

    /** Posts an error message to Barkeep49's talk page. */
    async function reportErrorToBarkeep49(errorType, errorDetails) {
        try {
            const talkPage = 'User talk:Barkeep49';
            const sectionTitle = 'RfA Starter error report';
            const timestamp = new Date().toISOString();
            const messageContent = `'''Error Type:''' ${errorType}\n\n` +
                `'''Page:''' [[${config.pageName}]]\n\n` +
                `'''RfX Type:''' ${config.rfxType}\n\n` +
                `'''User:''' [[User:${config.userName}]]\n\n` +
                `'''Timestamp:''' ${timestamp}\n\n` +
                `'''Error Details:'''\n${errorDetails}\n\n` +
                `~~${'~'.repeat(4)}~~`;
            
            const summary = `Reporting RfA Starter error${config.tagLine}`;
            
            await makeApiRequest({
                action: 'edit',
                title: talkPage,
                section: 'new',
                sectiontitle: sectionTitle,
                text: messageContent,
                summary: summary
            }, 'post', 'edit');
            
            return { success: true };
        } catch (error) {
            console.error('RfA Starter: Failed to report error to Barkeep49:', error);
            return { success: false, error: error.info || error.message };
        }
    }

    // --- Core Functions ---

    /** Removes the HTML comment that prevents template substitution. */
    async function removeSubstitutionComment() {
        try {
            const wikitext = await fetchPageWikitext(config.pageName);
            if (!wikitext) {
                const errorMsg = 'Could not fetch page wikitext';
                return { success: false, error: errorMsg, needsReporting: true };
            }

            let modifiedWikitext = wikitext;
            let foundComment = false;

            if (config.rfxType === 'adminship') {
                // For RfA: Remove <!-- To substitute your RfA, remove this line (the HTML comment markup here inactivates the safesubst code)-->
                const rfaCommentPattern = /<!--\s*To substitute your RfA, remove this line \(the HTML comment markup here inactivates the safesubst code\)\s*-->\s*\n?/g;
                if (rfaCommentPattern.test(wikitext)) {
                    foundComment = true;
                    modifiedWikitext = wikitext.replace(rfaCommentPattern, '');
                }
            } else {
                // For RfB: Remove comment around <!--subst:-->RfA/time... to make it subst:RfA/time...
                // Pattern: <!--subst:--> followed by RfA/time
                const rfbCommentPattern = /<!--subst:-->/g;
                if (rfbCommentPattern.test(wikitext)) {
                    foundComment = true;
                    modifiedWikitext = wikitext.replace(rfbCommentPattern, 'subst:');
                }
            }

            // Check if we found the comment pattern
            if (!foundComment && modifiedWikitext === wikitext) {
                const errorMsg = `Could not find substitution-blocking comment on ${config.pageName}. ` +
                    `Expected pattern for ${config.rfxType === 'adminship' ? 'RfA' : 'RfB'} not found.`;
                return { success: false, error: errorMsg, needsReporting: true };
            }

            // Only save if there was a change
            if (modifiedWikitext !== wikitext) {
                await makeApiRequest({
                    action: 'edit',
                    title: config.pageName,
                    text: modifiedWikitext,
                    summary: `Removing substitution-blocking comment${config.tagLine}`
                }, 'post', 'edit');
                return { success: true };
            } else {
                return { success: true, message: 'No substitution comment found (may already be removed)' };
            }
        } catch (error) {
            const errorMsg = `${error.info || error.message} (${error.code || 'unknown'})`;
            return { success: false, error: errorMsg, needsReporting: true };
        }
    }

    /** Adds transclusion to the main RfA/RfB page. */
    async function transcludeToMainPage() {
        try {
            const mainPageWikitext = await fetchPageWikitext(config.baseRfxPage);
            if (!mainPageWikitext) {
                const errorMsg = `Could not fetch main ${config.rfxType} page wikitext: ${config.baseRfxPage}`;
                return { success: false, error: errorMsg, needsReporting: true };
            }

            // Check if already transcluded
            const pageNameForRegex = escapeRegex(config.pageName);
            const transclusionPattern = new RegExp(`\\{\\{${pageNameForRegex}\\}\\}`, 'i');
            if (transclusionPattern.test(mainPageWikitext)) {
                return { success: false, error: 'Page is already transcluded', needsReporting: false };
            }

            // Find the insertion point
            let insertionIndex = -1;
            const transclusionLine = `{{${config.pageName}}}\n----\n`;
            let expectedMarker = '';

            if (config.rfxType === 'adminship') {
                // For RfA: Insert below <!--Please leave this horizontal rule and place RfA transclusion below-->
                const rfaMarker = /<!--Please leave this horizontal rule and place RfA transclusion below-->/;
                expectedMarker = '<!--Please leave this horizontal rule and place RfA transclusion below-->';
                const match = mainPageWikitext.match(rfaMarker);
                if (match) {
                    insertionIndex = match.index + match[0].length;
                }
            } else {
                // For RfB: Insert below <!-- Please leave this horizontal rule -->
                const rfbMarker = /<!--\s*Please leave this horizontal rule\s*-->/;
                expectedMarker = '<!-- Please leave this horizontal rule -->';
                const match = mainPageWikitext.match(rfbMarker);
                if (match) {
                    insertionIndex = match.index + match[0].length;
                }
            }

            if (insertionIndex === -1) {
                const errorMsg = `Could not find insertion marker on ${config.baseRfxPage}. ` +
                    `Expected marker: "${expectedMarker}"`;
                return { success: false, error: errorMsg, needsReporting: true };
            }

            // Insert the transclusion
            const modifiedWikitext = mainPageWikitext.substring(0, insertionIndex) + 
                                     '\n' + transclusionLine + 
                                     mainPageWikitext.substring(insertionIndex);

            await makeApiRequest({
                action: 'edit',
                title: config.baseRfxPage,
                text: modifiedWikitext,
                summary: `Adding transclusion for ${config.pageName}${config.tagLine}`
            }, 'post', 'edit');

            return { success: true };
        } catch (error) {
            const errorMsg = `${error.info || error.message} (${error.code || 'unknown'})`;
            return { success: false, error: errorMsg, needsReporting: true };
        }
    }

    // --- Event Handler ---

    /** Handles the submit button click. */
    async function handleSubmitClick(e) {
        e.preventDefault();
        const button = e.target;
        const originalText = button.textContent;
        
        // Show confirmation dialog
        const confirmMessage = `This will:\n\n` +
            `1. Remove the substitution-blocking comment from ${config.pageName}\n` +
            `2. Add the transclusion {{${config.pageName}}} to ${config.baseRfxPage}\n\n` +
            `Do you want to proceed?`;
        
        if (!confirm(confirmMessage)) {
            return; // User cancelled
        }
        
        button.disabled = true;
        button.textContent = 'Processing...';

        // Step 1: Remove substitution comment
        button.textContent = 'Removing comment...';
        const commentResult = await removeSubstitutionComment();
        
        if (!commentResult.success) {
            const errorMsg = `Error removing comment: ${commentResult.error}`;
            button.textContent = errorMsg;
            button.style.color = 'red';
            button.disabled = false;
            
            // Report error to Barkeep49 if needed
            if (commentResult.needsReporting) {
                button.textContent = errorMsg + ' (Reporting error...)';
                await reportErrorToBarkeep49(
                    'Failed to remove substitution comment',
                    `Error: ${commentResult.error}\n\nPage: ${config.pageName}\nRfX Type: ${config.rfxType}`
                );
                button.textContent = errorMsg + ' (Error reported)';
            }
            return;
        }

        // Step 2: Add transclusion
        button.textContent = 'Adding transclusion...';
        const transcludeResult = await transcludeToMainPage();
        
        if (!transcludeResult.success) {
            const errorMsg = `Error adding transclusion: ${transcludeResult.error}`;
            button.textContent = errorMsg;
            button.style.color = 'red';
            button.disabled = false;
            
            // Report error to Barkeep49 if needed
            if (transcludeResult.needsReporting) {
                button.textContent = errorMsg + ' (Reporting error...)';
                await reportErrorToBarkeep49(
                    'Failed to add transclusion',
                    `Error: ${transcludeResult.error}\n\nPage: ${config.pageName}\nMain Page: ${config.baseRfxPage}\nRfX Type: ${config.rfxType}`
                );
                button.textContent = errorMsg + ' (Error reported)';
            }
            return;
        }

        // Success
        button.textContent = 'Success!';
        button.style.color = 'green';
        setTimeout(() => {
            button.textContent = originalText;
            button.style.color = '';
            button.disabled = false;
        }, 3000);
    }

    // --- Initialization ---
    mw.loader.using(['mediawiki.api', 'mediawiki.util'], function() {
        // Create and add submit button
        const buttonText = config.rfxType === 'adminship' ? 'Submit RfA' : 'Submit RfB';
        const submitButton = document.createElement('a');
        submitButton.id = 'rfa-starter-submit';
        submitButton.textContent = buttonText;
        submitButton.href = '#';
        submitButton.style.color = '#0645ad';
        submitButton.style.textDecoration = 'none';
        submitButton.style.cursor = 'pointer';
        submitButton.addEventListener('click', handleSubmitClick);
        submitButton.addEventListener('mouseenter', function() {
            this.style.textDecoration = 'underline';
        });
        submitButton.addEventListener('mouseleave', function() {
            this.style.textDecoration = 'none';
        });

        const pageTools = document.querySelector('#p-tb ul');
        if (pageTools) {
            const li = document.createElement('li');
            li.id = 'rfa-starter-launch-li';
            li.appendChild(submitButton);
            pageTools.appendChild(li);
        }
    });

    console.log("RfA Starter: Script loaded.");

})();