import type * as React from 'react';
import * as ReactDOM from 'react-dom';
import semiGlobal from './semi-global';

const fullClone: Record<string, any> = { ...ReactDOM };
const legacyRender: any = fullClone.render;
const legacyUnmount: any = fullClone.unmountComponentAtNode;
const legacyFindDOMNode: any = fullClone.findDOMNode;

const { version } = ReactDOM;
const mainVersion = Number((version || '').split('.')[0]);

// React 版本兼容性检查
let hasWarnedVersionMismatch = false;
function checkVersionCompatibility() {
    if (hasWarnedVersionMismatch) {
        return;
    }
    // 如果是 React 19+ 但没有注入 createRoot，在首次使用时会通过 warnCreateRootNotFound 警告
    // 如果是 React < 18 但用户注入了 createRoot，给出警告
    if (mainVersion < 18 && typeof semiGlobal.config?.createRoot === 'function') {
        hasWarnedVersionMismatch = true;
        console.warn(
            `[Semi UI] createRoot was injected but React version is ${version} (< 18). ` +
            'This configuration is unusual and may cause unexpected behavior.'
        );
    }
}

function toggleWarning(skip: boolean) {
    const internals = fullClone.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
        ?? fullClone.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
    if (internals && typeof internals === 'object') {
        internals.usingClientEntryPoint = skip;
    }
}

/**
 * Resolve `createRoot` with 3-level fallback:
 * 1. semiGlobal.config.createRoot (user injection, required for React 19)
 * 2. fullClone.createRoot (auto-discovery from react-dom default export, works in React 18)
 * 3. undefined → triggers console.error guiding user to inject
 */
function resolveCreateRoot(): CreateRootFn | undefined {
    if (typeof semiGlobal.config?.createRoot === 'function') {
        return semiGlobal.config.createRoot as CreateRootFn;
    }
    if (typeof fullClone.createRoot === 'function') {
        return fullClone.createRoot;
    }
    return undefined;
}

let hasWarnedCreateRoot = false;

function warnCreateRootNotFound() {
    if (hasWarnedCreateRoot) {
        return;
    }
    hasWarnedCreateRoot = true;
    console.error(
        '[Semi UI] createRoot is not available. ' +
        'If you are using React 19, please inject createRoot before using Semi components. ' +
        'For details, see: https://semi.design/zh-CN/ecosystem/react19\n' +
        '[Semi UI] createRoot 不可用。' +
        '如果您正在使用 React 19，请在使用 Semi 组件前注入 createRoot。' +
        '详情请参阅：https://semi.design/zh-CN/ecosystem/react19'
    );
}

// ========================== Render ==========================

const MARK = '__semi_react_root__';

type CreateRootFn = (container: Element | DocumentFragment) => {
    render(children: React.ReactNode): void;
    unmount(): void;
};

type ContainerType = (Element | DocumentFragment) & {
    [MARK]?: ReturnType<CreateRootFn>;
};

export function render(node: React.ReactElement, container: ContainerType) {
    checkVersionCompatibility();
    const createRoot = resolveCreateRoot();
    if (createRoot) {
        toggleWarning(true);
        const root = container[MARK] || createRoot(container);
        toggleWarning(false);
        root.render(node);
        container[MARK] = root;
    } else if (legacyRender) {
        legacyRender(node, container);
    } else {
        warnCreateRootNotFound();
    }
}

export function unmount(container: ContainerType) {
    // 优先检查是否有 createRoot 创建的 root，而不是检查 createRoot 是否可用
    // 这样可以正确处理：用 legacyRender 渲染后，用户又注入了 createRoot 的情况
    if (container[MARK]) {
        container[MARK].unmount();
        delete container[MARK];
    } else if (legacyUnmount) {
        legacyUnmount(container);
    }
    // 如果既没有 root 也没有 legacyUnmount，可能是：
    // 1. 容器从未被渲染过
    // 2. 容器已经被卸载过了
    // 这两种情况都不需要警告，静默处理即可
}

// ======================== findDOMNode ========================

/**
 * React 19+ fallback for findDOMNode: traverse React Fiber tree downward
 * from a class component instance to find the first DOM element.
 * 
 * Uses React internal Fiber structure (_reactInternals). If React changes
 * its internals in future versions, this will safely return null without
 * throwing errors, falling back to the warning path in resolveDOM.
 */
function findDOMFromFiber(instance: any): Element | null {
    try {
        const fiber = instance?._reactInternals ?? instance?._reactInternalFiber;
        if (!fiber || typeof fiber !== 'object') {
            return null;
        }
        let node = fiber.child;
        let iterations = 0;
        const MAX_ITERATIONS = 50;
        while (node && iterations < MAX_ITERATIONS) {
            iterations++;
            // HostComponent: stateNode is a DOM element
            if (node.stateNode instanceof Element) {
                return node.stateNode;
            }
            if (node.child) {
                node = node.child;
                continue;
            }
            while (node && !node.sibling) {
                if (node === fiber) {
                    return null;
                }
                node = node.return;
            }
            if (node && node !== fiber) {
                node = node.sibling;
            } else {
                break;
            }
        }
    } catch (e) {
        // Fiber structure may have changed in a future React version; fail silently
    }
    return null;
}

/**
 * React 16/17/18: use ReactDOM.findDOMNode to resolve real DOM from component instance.
 * React 19: findDOMNode is removed; traverse Fiber tree to find DOM node.
 * 
 * 注意：findDOMNode 可能返回 Text 节点，但我们只返回 Element 类型以保证类型安全。
 */
export function resolveDOM(instance: any): Element | null {
    if (!instance) {
        return null;
    }
    // 已经是 Element，直接返回
    if (instance instanceof Element) {
        return instance;
    }
    // 尝试使用 findDOMNode (React 16/17/18)
    if (legacyFindDOMNode) {
        try {
            const node = legacyFindDOMNode(instance as React.ReactInstance);
            // findDOMNode 可能返回 Text 节点，我们只返回 Element
            if (node instanceof Element) {
                return node;
            }
            return null;
        } catch (e) {
            // findDOMNode 可能在某些情况下抛出错误（如 StrictMode 警告）
            return null;
        }
    }
    // React 19 fallback: traverse Fiber tree to find the first DOM element
    return findDOMFromFiber(instance);
}

// ========================= getRef ===========================

/**
 * React 16/17/18: ref is a top-level property on the element (element.ref).
 * React 19: ref is moved into element.props.ref.
 * 
 * 使用版本检测来确定 ref 的位置，避免在 React 18 中错误地读取 props.ref。
 */
export function getRef(element: any): React.Ref<any> | null {
    if (!element) {
        return null;
    }
    // React 19+: ref 在 props 中
    if (mainVersion >= 19) {
        return element.props?.ref ?? null;
    }
    // React 16/17/18: ref 在顶层
    return element.ref ?? null;
}
