/* eslint-disable no-use-before-define */
import { getAllApp, getAppFromInstance } from '../global';
import { MainMustardApp, MustardName, MustardURL } from '../typings';
import { getLocationNameByAppName } from '../utils';
import { isFunction, isMustardState, isURL } from '../utils/tools';

type ChangeState = (_state:unknown, _unused:string, _url:string | URL, _isMustard?:boolean) => void;

export interface MustardStateOptions {
    origin?:MustardURL, // document 来源
    flushed?:boolean // 是否更新文档
}

export interface MustardState extends MustardStateOptions{
    data?: unknown, // pushState 和 replaceState 第一个参数
    index: number, // 用于计算当前state 的顺序
}

export type State = {
    [key: string]: MustardState;
} & {
    isMustard: 'MustardApp';
    [MainMustardApp]: undefined
}

export function encodeState (data:unknown, appName:MustardName, options?:MustardStateOptions) {
    const app = getAppFromInstance(appName);
    const index = getStateIndex(appName) + 1;
    // todo origin
    const { flushed = false, origin = app?.state?.origin } = options ?? {};
    const allAppState = getAllAppState();
    return {
        ...allAppState,
        [appName]: {
            data,
            index,
            origin,
            flushed            
        }
    };
}

export function decodeState (appName:MustardName):MustardState|undefined {
    const state = history.state;
    if(state?.[appName]) {
        return state[appName];
    }
}

export function getAllAppState () {
    const state = {
        isMustard: 'MustardApp',
        [MainMustardApp]: undefined
    } as State;

    getAllApp().forEach(name => {
        if(decodeState(name)) {
            state[name] = decodeState(name);
        }
    });

    return state;
}

export function getStateIndex (appName:MustardName) {
    return decodeState(appName)?.index ?? 0;
}

export function initState (appName: MustardName, state:unknown, unused:string, url:string) {
    let preState = history.state;
    if(!isMustardState(preState)) {
        preState = {
            isMustard: 'MustardApp',
            [MainMustardApp]: preState
        };
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    (history.replaceState as any as ChangeState)({ 
        ...preState,
        [appName]: {
            index: 0,
            origin: url
        }
    }, unused, undefined, true);
}

export function navigateTo (appName:MustardName, type: 'pushState'|'replaceState', flushed?:boolean) {
    const navigateToMehtods = function (_state:unknown, _unused:string, _url?:string | URL) {
        return history[type].call(history, _state, _unused, _url, true);
    };
    const pathKey = getLocationNameByAppName(appName);
    return function (_state:unknown, _unused:string, _url?:string | URL) {
        const app = getAppFromInstance(appName);
        const preState = decodeState(appName);

        if(!_url) {
            // 不刷新
            return navigateToMehtods(encodeState(_state, appName, {
                flushed: type === 'replaceState' ? preState?.flushed : flushed
            }), _unused);
        }

        // 处理_url：相对地址->绝对地址
        const url = new URL(isURL(_url) ? _url.href : _url, app.url);

        const state = encodeState(_state, appName, {
            flushed: type === 'replaceState' ? preState?.flushed : flushed,
            origin: !flushed ? app.state.origin : url.href // 不刷新页面 使用当前 app.state.origin，否则使用跳转的地址做文档来源
        });

        const { pathname: pathnameFromLocation, search: searchFromLocation = '', hash: hashFromLocation } = location;

        const searchFromLocationParams = new URLSearchParams(searchFromLocation);
        
        searchFromLocationParams.set(pathKey, encodeURIComponent(url.href)); // 设置app对应的地址
        const searchParams = searchFromLocationParams.toString();

        app.state = state[appName];

        return navigateToMehtods(state, _unused, `${pathnameFromLocation}${searchParams ? '?' + searchParams : ''}${hashFromLocation}`);
    };
}

export function proxyHistory (appName:MustardName) {
    return new Proxy(history, {
        get (target, key) {
            if(key === 'pushState') {
                return navigateTo(appName, 'pushState');
            }else if(key === 'replaceState') {
                return navigateTo(appName, 'replaceState');
            }else if(key === 'state') {
                return decodeState(appName)?.data;
            }else if(isFunction(target[key])) {
                return target[key].bind(history);
            }
            return target[key];
        }
    });
}

// 修改全局history方法
export function changeHistoryPropety () {
    const pushState = History.prototype.pushState;
    const replaceState = History.prototype.replaceState;

    function changeState (type:'pushState'|'replaceState') {
        const methodState = type === 'pushState' ? pushState : replaceState;
        return function (_state:unknown, _unused:string, _url:string | URL, _isMustard?:boolean) {
            if(_isMustard) {
                return methodState.call(this, _state, _unused, _url);
            }else{
                const allAppState = getAllAppState();
                return methodState.call(this, { ...allAppState, [MainMustardApp]: _state }, _unused, _url);
            }
        };
    }
    
    History.prototype.pushState = changeState('pushState');
    History.prototype.replaceState = changeState('replaceState');
}