import { Component, options } from "@lynx-js/react/internal";
const NAMESPACE = '__PREFRESH__';
const COMPONENT_DIRTY_BIT = 8;
const isDirty = (vnode)=>{
    if (vnode["__c"] && vnode["__c"]["__d"]) return true;
    if (vnode["__c"] && vnode["__c"]["__g"] & COMPONENT_DIRTY_BIT) return true;
};
const unsetDirty = (vnode)=>{
    if (vnode["__c"]) {
        if (vnode["__c"]["__d"]) vnode["__c"]["__d"] = false;
        if (vnode["__c"]["__g"]) vnode["__c"]["__g"] &= ~COMPONENT_DIRTY_BIT;
    }
};
const oldCatchError = options["__e"];
options["__e"] = (error, vnode, oldVNode, info)=>{
    if (isDirty(vnode)) unsetDirty(vnode);
    if (oldCatchError) oldCatchError(error, vnode, oldVNode, info);
};
const defer = 'function' == typeof Promise ? Promise.prototype.then.bind(Promise.resolve()) : setTimeout;
options.debounceRendering = (process)=>{
    defer(()=>{
        try {
            process();
        } catch (e) {
            process["__r"] = 0;
            throw e;
        }
    });
};
const vnodesForComponent = new WeakMap();
const mappedVNodes = new WeakMap();
const lastSeen = new Map();
const getMappedVnode = (type)=>{
    if (mappedVNodes.has(type)) return getMappedVnode(mappedVNodes.get(type));
    return type;
};
const BUILT_IN_COMPONENTS = [
    'Fragment',
    'Suspense',
    'SuspenseList'
];
const isBuiltIn = (type)=>BUILT_IN_COMPONENTS.includes(type.name);
const oldVnode = options.vnode;
options.vnode = (vnode)=>{
    if (vnode && 'function' == typeof vnode.type && !isBuiltIn(vnode.type)) {
        const foundType = getMappedVnode(vnode.type);
        if (foundType !== vnode.type) {
            vnode.type = foundType;
            if (vnode["__c"] && 'prototype' in vnode.type && vnode.type.prototype.render) vnode["__c"].constructor = vnode.type;
        }
    }
    if (oldVnode) oldVnode(vnode);
};
const oldDiff = options.__b;
options.__b = (vnode)=>{
    if (vnode && 'function' == typeof vnode.type && !isBuiltIn(vnode.type)) {
        const vnodes = vnodesForComponent.get(vnode.type);
        if (vnodes) vnodes.push(vnode);
        else vnodesForComponent.set(vnode.type, [
            vnode
        ]);
    }
    if (oldDiff) oldDiff(vnode);
};
const oldDiffed = options.diffed;
options.diffed = (vnode)=>{
    if (vnode && 'function' == typeof vnode.type) {
        const vnodes = vnodesForComponent.get(vnode.type);
        lastSeen.set(vnode.__v, vnode);
        if (vnodes) {
            const matchingDom = vnodes.filter((p)=>p.__c === vnode.__c);
            if (matchingDom.length > 1) {
                const i = vnodes.findIndex((p)=>p === matchingDom[0]);
                vnodes.splice(i, 1);
            }
        }
    }
    if (oldDiffed) oldDiffed(vnode);
};
const oldUnmount = options.unmount;
options.unmount = (vnode)=>{
    const type = (vnode || {}).type;
    if ('function' == typeof type && vnodesForComponent.has(type)) {
        const vnodes = vnodesForComponent.get(type);
        if (vnodes) {
            const index = vnodes.indexOf(vnode);
            if (-1 !== index) vnodes.splice(index, 1);
        }
    }
    if (oldUnmount) oldUnmount(vnode);
};
const signaturesForType = new WeakMap();
const computeKey = (signature)=>{
    let fullKey = signature.key;
    let hooks;
    try {
        hooks = signature.getCustomHooks();
    } catch (err) {
        signature.forceReset = true;
        return fullKey;
    }
    for(let i = 0; i < hooks.length; i++){
        const hook = hooks[i];
        if ('function' != typeof hook) {
            signature.forceReset = true;
            break;
        }
        const nestedHookSignature = signaturesForType.get(hook);
        if (void 0 === nestedHookSignature) continue;
        const nestedHookKey = computeKey(nestedHookSignature);
        if (nestedHookSignature.forceReset) signature.forceReset = true;
        fullKey += '\n---\n' + nestedHookKey;
    }
    return fullKey;
};
let typesById = new Map();
let pendingUpdates = [];
function sign(type, key, forceReset, getCustomHooks, status) {
    if (type) {
        let signature = signaturesForType.get(type);
        if ('begin' === status) {
            signaturesForType.set(type, {
                type,
                key,
                forceReset,
                getCustomHooks: getCustomHooks || (()=>[])
            });
            return 'needsHooks';
        }
        if ('needsHooks' === status) signature.fullKey = computeKey(signature);
    }
}
function replaceComponent(OldType, NewType, resetHookState) {
    const vnodes = vnodesForComponent.get(OldType);
    if (!vnodes) return;
    vnodesForComponent["delete"](OldType);
    vnodesForComponent.set(NewType, vnodes);
    mappedVNodes.set(OldType, NewType);
    pendingUpdates = pendingUpdates.filter((p)=>p[0] !== OldType);
    vnodes.forEach((node)=>{
        let vnode = node;
        if (vnode && vnode.__v && !vnode.__c && lastSeen.has(vnode.__v)) {
            vnode = lastSeen.get(vnode.__v);
            lastSeen["delete"](vnode.__v);
        }
        if (!vnode || !vnode.__c || !vnode.__c.__P) return;
        vnode.type = NewType;
        if (vnode["__c"]) {
            vnode["__c"].constructor = vnode.type;
            try {
                if (vnode["__c"] instanceof OldType) {
                    const oldInst = vnode["__c"];
                    const newInst = new NewType(vnode["__c"].props, vnode["__c"].context);
                    vnode["__c"] = newInst;
                    for(let i in oldInst){
                        const type = typeof oldInst[i];
                        if (i in newInst) {
                            if ('function' !== type && typeof newInst[i] === type) if ('object' === type && null != newInst[i] && newInst[i].constructor === oldInst[i].constructor) Object.assign(newInst[i], oldInst[i]);
                            else newInst[i] = oldInst[i];
                        } else newInst[i] = oldInst[i];
                    }
                }
            } catch (e) {
                vnode["__c"].constructor = NewType;
            }
            if (resetHookState) {
                if (vnode["__c"]["__H"] && vnode["__c"]["__H"]["__"] && vnode["__c"]["__H"]["__"].length) vnode["__c"]["__H"]["__"].forEach((possibleEffect)=>{
                    if (possibleEffect["__c"] && 'function' == typeof possibleEffect["__c"]) {
                        possibleEffect["__c"]();
                        possibleEffect["__c"] = void 0;
                    } else if (possibleEffect["__H"] && possibleEffect["__"] && 3 === Object.keys(possibleEffect).length) {
                        const cleanupKey = Object.keys(possibleEffect).find((key)=>"__H" !== key && "__" !== key);
                        if (cleanupKey && 'function' == typeof possibleEffect[cleanupKey]) {
                            possibleEffect[cleanupKey]();
                            possibleEffect[cleanupKey] = void 0;
                        }
                    }
                });
                vnode["__c"]["__H"] = {
                    ["__"]: [],
                    ["__h"]: []
                };
            } else if (vnode["__c"]["__H"] && vnode["__c"]["__H"]["__"] && vnode["__c"]["__H"]["__"].length) {
                vnode["__c"]["__H"]["__"].forEach((possibleEffect)=>{
                    if (possibleEffect["__c"] && 'function' == typeof possibleEffect["__c"]) {
                        possibleEffect["__c"]();
                        possibleEffect["__c"] = void 0;
                    } else if (possibleEffect["__H"] && possibleEffect["__"] && 3 === Object.keys(possibleEffect).length) {
                        const cleanupKey = Object.keys(possibleEffect).find((key)=>"__H" !== key && "__" !== key);
                        if (cleanupKey && 'function' == typeof possibleEffect[cleanupKey]) possibleEffect[cleanupKey]();
                        possibleEffect[cleanupKey] = void 0;
                    }
                });
                vnode["__c"]["__H"]["__"].forEach((hook)=>{
                    if (hook.__H && Array.isArray(hook.__H)) hook.__H = void 0;
                });
            }
            Component.prototype.forceUpdate.call(vnode["__c"]);
        }
    });
}
globalThis[NAMESPACE] = {
    getSignature: (type)=>signaturesForType.get(type),
    register: (type, id)=>{
        if ('function' != typeof type) return;
        if (typesById.has(id)) {
            const existing = typesById.get(id);
            if (existing !== type) {
                pendingUpdates.push([
                    existing,
                    type
                ]);
                typesById.set(id, type);
            }
        } else typesById.set(id, type);
        if (!signaturesForType.has(type)) signaturesForType.set(type, {
            getCustomHooks: ()=>[],
            type
        });
    },
    getPendingUpdates: ()=>pendingUpdates,
    flush: ()=>{
        pendingUpdates = [];
    },
    replaceComponent,
    sign,
    computeKey: computeKey
};
const compareSignatures = (prev, next)=>{
    const prevSignature = globalThis.__PREFRESH__.getSignature(prev) || {};
    const nextSignature = globalThis.__PREFRESH__.getSignature(next) || {};
    if (prevSignature.key !== nextSignature.key || globalThis.__PREFRESH__.computeKey(prevSignature) !== globalThis.__PREFRESH__.computeKey(nextSignature) || nextSignature.forceReset) globalThis.__PREFRESH__.replaceComponent(prev, next, true);
    else globalThis.__PREFRESH__.replaceComponent(prev, next, false);
};
const flush = ()=>{
    const pending = [
        ...globalThis.__PREFRESH__.getPendingUpdates()
    ];
    globalThis.__PREFRESH__.flush();
    if (pending.length > 0) pending.forEach(([prev, next])=>{
        compareSignatures(prev, next);
    });
};
const isComponent = (exportValue)=>{
    if ('function' == typeof exportValue) {
        if (null != exportValue.prototype && exportValue.prototype.isReactComponent) return true;
        const name = exportValue.name || exportValue.displayName;
        return 'string' == typeof name && name[0] && name[0] == name[0].toUpperCase();
    }
    return false;
};
export { flush, isComponent };
