import { emitter } from "@rspack/core/hot/emitter.js";
import { log } from "@rspack/core/hot/log.js";
import { createOverlay, formatProblem } from "./overlay.js";
import socket from "./socket.js";
import { defineProgressElement, isProgressSupported } from "./progress.js";
import { log as log_js_log, setLogLevel } from "./utils/log.js";
import sendMessage from "./utils/sendMessage.js";
function _define_property(obj, key, value) {
    if (key in obj) Object.defineProperty(obj, key, {
        value: value,
        enumerable: true,
        configurable: true,
        writable: true
    });
    else obj[key] = value;
    return obj;
}
function _object_spread(target) {
    for(var i = 1; i < arguments.length; i++){
        var source = null != arguments[i] ? arguments[i] : {};
        var ownKeys = Object.keys(source);
        if ("function" == typeof Object.getOwnPropertySymbols) ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
            return Object.getOwnPropertyDescriptor(source, sym).enumerable;
        }));
        ownKeys.forEach(function(key) {
            _define_property(target, key, source[key]);
        });
    }
    return target;
}
const decodeOverlayOptions = (overlayOptions)=>{
    if ('object' == typeof overlayOptions) [
        'warnings',
        'errors',
        'runtimeErrors'
    ].forEach((property)=>{
        if ('string' == typeof overlayOptions[property]) {
            const overlayFilterFunctionString = decodeURIComponent(overlayOptions[property]);
            overlayOptions[property] = new Function('message', `var callback = ${overlayFilterFunctionString}
				return callback(message)`);
        }
    });
};
const parseURL = (resourceQuery)=>{
    let result = {};
    if ('string' == typeof resourceQuery && '' !== resourceQuery) {
        const searchParams = resourceQuery.slice(1).split('&');
        for(let i = 0; i < searchParams.length; i++){
            const pair = searchParams[i].split('=');
            result[pair[0]] = decodeURIComponent(pair[1]);
        }
    } else {
        const scriptSource = getCurrentScriptSource();
        let scriptSourceURL;
        try {
            scriptSourceURL = new URL(scriptSource, self.location.href);
        } catch (error) {}
        if (scriptSourceURL) {
            result = scriptSourceURL;
            result['fromCurrentScript'] = 'true';
        }
    }
    return result;
};
const client_src_status = {
    isUnloading: false,
    currentHash: __webpack_hash__
};
const getCurrentScriptSource = ()=>{
    if (document.currentScript) return document.currentScript.getAttribute('src');
    const scriptElements = document.scripts || [];
    const scriptElementsWithSrc = Array.prototype.filter.call(scriptElements, (element)=>element.getAttribute('src'));
    if (scriptElementsWithSrc.length > 0) {
        const currentScript = scriptElementsWithSrc[scriptElementsWithSrc.length - 1];
        return currentScript.getAttribute('src');
    }
    throw new Error("[rspack-dev-server] Failed to get current script source.");
};
const parsedResourceQuery = parseURL(__resourceQuery);
const enabledFeatures = {
    'Hot Module Replacement': false,
    'Live Reloading': false,
    Progress: false,
    Overlay: false
};
const options = {
    hot: false,
    liveReload: false,
    progress: false,
    overlay: false
};
if ('true' === parsedResourceQuery.hot) {
    options.hot = true;
    enabledFeatures['Hot Module Replacement'] = true;
}
if ('true' === parsedResourceQuery['live-reload']) {
    options.liveReload = true;
    enabledFeatures['Live Reloading'] = true;
}
if ('true' === parsedResourceQuery.progress) {
    options.progress = true;
    enabledFeatures.Progress = true;
}
if (parsedResourceQuery.overlay) {
    try {
        options.overlay = JSON.parse(parsedResourceQuery.overlay);
    } catch (e) {
        log_js_log.error('Error parsing overlay options from resource query:', e);
    }
    if ('object' == typeof options.overlay) {
        options.overlay = _object_spread({
            errors: true,
            warnings: true,
            runtimeErrors: true
        }, options.overlay);
        decodeOverlayOptions(options.overlay);
    }
    enabledFeatures.Overlay = false !== options.overlay;
}
if (parsedResourceQuery.logging) options.logging = parsedResourceQuery.logging;
if (void 0 !== parsedResourceQuery.reconnect) options.reconnect = Number(parsedResourceQuery.reconnect);
const setAllLogLevel = (level)=>{
    log.setLogLevel('verbose' === level || 'log' === level ? 'info' : level);
    setLogLevel(level);
};
if (options.logging) setAllLogLevel(options.logging);
const logEnabledFeatures = (features)=>{
    const listEnabledFeatures = Object.keys(features);
    if (!features || 0 === listEnabledFeatures.length) return;
    let logString = 'Server started:';
    for(let i = 0; i < listEnabledFeatures.length; i++){
        const key = listEnabledFeatures[i];
        logString += ` ${key} ${features[key] ? 'enabled' : 'disabled'},`;
    }
    logString = logString.slice(0, -1).concat('.');
    log_js_log.info(logString);
};
logEnabledFeatures(enabledFeatures);
self.addEventListener('beforeunload', ()=>{
    client_src_status.isUnloading = true;
});
const overlay = "u" > typeof window ? createOverlay('object' == typeof options.overlay ? {
    trustedTypesPolicyName: options.overlay.trustedTypesPolicyName,
    catchRuntimeError: options.overlay.runtimeErrors
} : {
    trustedTypesPolicyName: false,
    catchRuntimeError: options.overlay
}) : {
    send: ()=>{}
};
const reloadApp = ({ hot, liveReload }, currentStatus)=>{
    if (currentStatus.isUnloading) return;
    const { currentHash, previousHash } = currentStatus;
    const isInitial = currentHash.indexOf(previousHash) >= 0;
    if (isInitial) return;
    function applyReload(rootWindow, intervalId) {
        clearInterval(intervalId);
        log_js_log.info('App updated. Reloading...');
        rootWindow.location.reload();
    }
    const search = self.location.search.toLowerCase();
    const allowToHot = -1 === search.indexOf('rspack-dev-server-hot=false');
    const allowToLiveReload = -1 === search.indexOf('rspack-dev-server-live-reload=false');
    if (hot && allowToHot) {
        log_js_log.info('App hot update...');
        emitter.emit('webpackHotUpdate', currentStatus.currentHash);
        if ("u" > typeof self && self.window) self.postMessage(`webpackHotUpdate${currentStatus.currentHash}`, '*');
    } else if (liveReload && allowToLiveReload) {
        let rootWindow = self;
        const intervalId = self.setInterval(()=>{
            if ('about:' !== rootWindow.location.protocol) applyReload(rootWindow, intervalId);
            else {
                rootWindow = rootWindow.parent;
                if (rootWindow.parent === rootWindow) applyReload(rootWindow, intervalId);
            }
        });
    }
};
const ansiRegex = new RegExp("[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))", 'g');
const stripAnsi = (string)=>{
    if ('string' != typeof string) throw new TypeError(`Expected a \`string\`, got \`${typeof string}\``);
    return string.replace(ansiRegex, '');
};
const onSocketMessage = {
    hot () {
        if ('false' === parsedResourceQuery.hot) return;
        options.hot = true;
    },
    liveReload () {
        if ('false' === parsedResourceQuery['live-reload']) return;
        options.liveReload = true;
    },
    invalid () {
        log_js_log.info('App updated. Recompiling...');
        if (options.overlay) overlay.send({
            type: 'DISMISS'
        });
        sendMessage('Invalid');
    },
    hash: function(_hash) {
        if (!_hash) return;
        client_src_status.previousHash = client_src_status.currentHash;
        client_src_status.currentHash = _hash;
    },
    logging: setAllLogLevel,
    overlay (value) {
        if ("u" < typeof document) return;
        options.overlay = value;
        decodeOverlayOptions(options.overlay);
    },
    reconnect (value) {
        if ('false' === parsedResourceQuery.reconnect) return;
        options.reconnect = value;
    },
    progress (value) {
        options.progress = value;
    },
    'progress-update': function(data) {
        if (options.progress) log_js_log.info(`${data.percent}% - ${data.msg}.`);
        if (isProgressSupported()) {
            if ('string' == typeof options.progress) {
                let progress = document.querySelector('wds-progress');
                if (!progress) {
                    defineProgressElement();
                    progress = document.createElement('wds-progress');
                    document.body.appendChild(progress);
                }
                progress.setAttribute('progress', data.percent.toString());
                progress.setAttribute('type', options.progress);
            }
        }
        sendMessage('Progress', data);
    },
    'still-ok': function() {
        log_js_log.info('Nothing changed.');
        if (options.overlay) overlay.send({
            type: 'DISMISS'
        });
        sendMessage('StillOk');
    },
    ok () {
        sendMessage('Ok');
        if (options.overlay) overlay.send({
            type: 'DISMISS'
        });
        reloadApp(options, client_src_status);
    },
    'static-changed': function(file) {
        log_js_log.info(`${file ? `"${file}"` : 'Content'} from static directory was changed. Reloading...`);
        self.location.reload();
    },
    warnings (warnings, params) {
        log_js_log.warn('Warnings while compiling.');
        const printableWarnings = warnings.map((error)=>{
            const { header, body } = formatProblem('warning', error);
            return `${header}\n${stripAnsi(body)}`;
        });
        sendMessage('Warnings', printableWarnings);
        for(let i = 0; i < printableWarnings.length; i++)log_js_log.warn(printableWarnings[i]);
        const overlayWarningsSetting = 'boolean' == typeof options.overlay ? options.overlay : options.overlay && options.overlay.warnings;
        if (overlayWarningsSetting) {
            const warningsToDisplay = 'function' == typeof overlayWarningsSetting ? warnings.filter(overlayWarningsSetting) : warnings;
            if (warningsToDisplay.length) overlay.send({
                type: 'BUILD_ERROR',
                level: 'warning',
                messages: warnings
            });
        }
        if (params && params.preventReloading) return;
        reloadApp(options, client_src_status);
    },
    errors (errors) {
        log_js_log.error('Errors while compiling. Reload prevented.');
        const printableErrors = errors.map((error)=>{
            const { header, body } = formatProblem('error', error);
            return `${header}\n${stripAnsi(body)}`;
        });
        sendMessage('Errors', printableErrors);
        for(let i = 0; i < printableErrors.length; i++)log_js_log.error(printableErrors[i]);
        const overlayErrorsSettings = 'boolean' == typeof options.overlay ? options.overlay : options.overlay && options.overlay.errors;
        if (overlayErrorsSettings) {
            const errorsToDisplay = 'function' == typeof overlayErrorsSettings ? errors.filter(overlayErrorsSettings) : errors;
            if (errorsToDisplay.length) overlay.send({
                type: 'BUILD_ERROR',
                level: 'error',
                messages: errors
            });
        }
    },
    error (error) {
        log_js_log.error(error);
    },
    close () {
        log_js_log.info('Disconnected!');
        if (options.overlay) overlay.send({
            type: 'DISMISS'
        });
        sendMessage('Close');
    }
};
const formatURL = (objURL)=>{
    let protocol = objURL.protocol || '';
    if (protocol && ':' !== protocol.substr(-1)) protocol += ':';
    let auth = objURL.auth || '';
    if (auth) {
        auth = encodeURIComponent(auth);
        auth = auth.replace(/%3A/i, ':');
        auth += '@';
    }
    let host = '';
    if (objURL.hostname) {
        host = auth + (-1 === objURL.hostname.indexOf(':') ? objURL.hostname : `[${objURL.hostname}]`);
        if (objURL.port) host += `:${objURL.port}`;
    }
    let pathname = objURL.pathname || '';
    if (objURL.slashes) {
        host = `//${host || ''}`;
        if (pathname && '/' !== pathname.charAt(0)) pathname = `/${pathname}`;
    } else if (!host) host = '';
    let search = objURL.search || '';
    if (search && '?' !== search.charAt(0)) search = `?${search}`;
    let hash = objURL.hash || '';
    if (hash && '#' !== hash.charAt(0)) hash = `#${hash}`;
    pathname = pathname.replace(/[?#]/g, (match)=>encodeURIComponent(match));
    search = search.replace('#', '%23');
    return `${protocol}${host}${pathname}${search}${hash}`;
};
const createSocketURL = (parsedURL)=>{
    let { hostname } = parsedURL;
    const isInAddrAny = '0.0.0.0' === hostname || '::' === hostname || '[::]' === hostname;
    if (isInAddrAny && self.location.hostname && 0 === self.location.protocol.indexOf('http')) hostname = self.location.hostname;
    let socketURLProtocol = parsedURL.protocol || self.location.protocol;
    if ('auto:' === socketURLProtocol || hostname && isInAddrAny && 'https:' === self.location.protocol) socketURLProtocol = self.location.protocol;
    socketURLProtocol = socketURLProtocol.replace(/^(?:http|.+-extension|file)/i, 'ws');
    let socketURLAuth = '';
    if (parsedURL.username) {
        socketURLAuth = parsedURL.username;
        if (parsedURL.password) socketURLAuth = socketURLAuth.concat(':', parsedURL.password);
    }
    const socketURLHostname = (hostname || self.location.hostname || 'localhost').replace(/^\[(.*)\]$/, '$1');
    let socketURLPort = parsedURL.port;
    if (!socketURLPort || '0' === socketURLPort) socketURLPort = self.location.port;
    let socketURLPathname = '/ws';
    if (parsedURL.pathname && !parsedURL.fromCurrentScript) socketURLPathname = parsedURL.pathname;
    return formatURL({
        protocol: socketURLProtocol,
        auth: socketURLAuth,
        hostname: socketURLHostname,
        port: socketURLPort,
        pathname: socketURLPathname,
        slashes: true
    });
};
const socketURL = createSocketURL(parsedResourceQuery);
socket(socketURL, onSocketMessage, options.reconnect);
export { createSocketURL, getCurrentScriptSource, parseURL };
