import { consumption, getAppFromInstance, mustardAppInfos } from '../global';
import { LocationPrefix, MustardName, MustardURL, TFunction } from '../typings';
import { scopedCSSTextContent } from './scopedcss';
import { isFunction, isRelativePath, isRemotezElement } from './tools';

/**
 * 获取虚拟路由key
 * @param appName 
 * @returns 
 */
export function getLocationNameByAppName (appName:MustardName) {
    return LocationPrefix + appName;
}

/**
 * 根据相对地址和当前页面地址返回具体资源路径
 * @param relativePath 相对地址
 * @param absolutePath 当前页面地址
 * @returns 
 */
export function getCompletePath (relativePath: string, absolutePath?: string) {
    if (!absolutePath || !isRelativePath(relativePath)) return relativePath;
    return new URL(relativePath, absolutePath).href;
}

/**
 * 请求资源
 * @param relativePath 相对地址
 * @param absolutePath 当前页面地址
 * @returns 
 */
export function fetchSource (relativePath: string, absolutePath?: string) {
    return fetch(getCompletePath(relativePath, absolutePath)).then((res) => {
        return res.text();
    });
}

/**
 * 监听Dom变化
 * @param dom 需要监听的dom元素
 * @param config 需要监听的范围 e.g 属性变动/子节点变动
 * @param callback 监听变动回调函数
 */
export function mutationObserver (dom:Element, config:MutationObserverInit, callback:MutationCallback) {
    const observer = new MutationObserver((mutationsList, observer) => {
        observer.disconnect();
        callback(mutationsList, observer);
        observer.observe(dom, config);
    });

    // 以上述配置开始观察目标节点
    observer.observe(dom, config);

    return observer;
}

/**
 * 处理子应用的dom
 * 1. 加上子应用标识 appName
 * 2. 修改ownerDocument，代理到proxydocument
 * 3. 特殊dom，特殊处理 e.g 1. 远程资源src 2. 动态style处理（实时加入前缀）
 * @param dom 
 * @param _appName 子应用标识 
 * @returns 
 */
export function handleDom<T extends Element> (dom:T, _appName?:MustardName):T {
    if(!dom) return dom;
    const appName = _appName ?? consumption();

    if(appName && !(dom as unknown as {appName?:string})?.appName) {
        const app = getAppFromInstance(appName);
        const proxyWindow = mustardAppInfos.getAppProxyWindow(appName);

        const config:PropertyDescriptorMap = {
            // 1. 子应用标识
            // 2. 判断是否处理过的元素
            appName: {
                value: appName
            },
            ownerDocument: {
                enumerable: true,
                get () {
                    return proxyWindow?.document ?? document;
                }
            }
        };

        // 远程资源地址适配
        if(isRemotezElement(dom)) {
            mutationObserver(dom, { attributes: true, attributeFilter: ['src'] }, function ([mutations] = []) {
                if(mutations.type === 'attributes') {
                    const target = mutations.target as HTMLImageElement;
                    target.src = getCompletePath(target.getAttribute('src'), app.url);
                }
            });
        }

        // 动态style适配
        if(dom instanceof HTMLStyleElement) {
            mutationObserver(dom, { childList: true }, function ([mutations] = []) {
                if(mutations.type === 'childList') {
                    mutations.target.textContent = scopedCSSTextContent(mutations.target.textContent, appName);
                }
            });
        }
        return Object.defineProperties(dom, config);
    }

    return dom;
}

/**
 * 处理选择器
 * e.g.
 *  1. head -> mustard-app-head
 *  2. body -> mustard-app-body
 * @param selectors 
 */
export function handleSelectors (selectors:string) {
    if(!selectors) return '';
    if(selectors?.trim() === 'head') return 'mustard-app-head';
    if(selectors?.trim() === 'body') return 'mustard-app-body';
    return selectors.split(',').map(
        _selector => _selector.replace(/(^|,|\s)(head|body)([^a-zA-Z]|$)/g, function (test) {
            return test.replace(/(head|body)/, (_, $1)=> `mustard-app-${$1}`);
        })
    ).join(',');
}

/**
 * 获取相对地址
 * 根据子应用的appName，从loaction.search 上读取对应数据
 * @param appName 
 * @returns 
 */
export function getPath (appName:MustardName) {
    const search = location.search;
    const searchParams = new URLSearchParams(search);
    const href = searchParams.get(`${LocationPrefix}${appName}`) ?? '/';
    return decodeURIComponent(href);
}

/**
 * 获取地址的URL对象
 * @param appName 
 * @param baseUrl 
 * @returns 
 */
export function getURL (appName:MustardName, baseUrl:MustardURL) {
    return new URL(getPath(appName), baseUrl);
}

/**
 * 
 * @param path 子应用地址路径
 * @param appName 子应用标识
 * @param location // 父应用或基座location
 * @returns 
 */
export function getNewPathToMustard (path:string, appName:MustardName) {
    const { pathname: pathnameFromTop, search: searchFromTop = '', hash: hashFromTop } = location; 
    
    const SearchParams = new URLSearchParams(searchFromTop);

    SearchParams.set(LocationPrefix + appName, encodeURIComponent(path));

    const searchParams = SearchParams.toString();
    return `${pathnameFromTop}${searchParams ? '?' + searchParams : ''}${hashFromTop}`;
}

/**
 * 异步下一微任务运行
 * @param fn 待运行的方法
 */
export function nextTick (fn: TFunction) {
    Promise.resolve().then(() => {
        isFunction(fn) && fn();
    });
}