"use client";
import { useContext, useState, useEffect, useRef, useOptimistic, startTransition, } from "react";
import { NavigationStoreContext } from "./context.js";
import { invariant } from "../../errors.js";
/**
 * Default action state (idle with no payload)
 */
const DEFAULT_ACTION_STATE = {
    state: "idle",
    actionId: null,
    payload: null,
    error: null,
    result: null,
};
/**
 * Normalize action ID - returns the ID as-is
 *
 * Server actions have IDs like "hash#actionName" or "src/actions.ts#actionName".
 * When using function references, we use the full ID for exact matching.
 * When using strings, the event controller supports suffix matching
 * (e.g., "addToCart" matches "hash#addToCart").
 */
function normalizeActionId(actionId) {
    return actionId;
}
/**
 * Extract action ID from a server action function or string.
 *
 * Actions passed as props from server components lose their metadata
 * during RSC serialization - use a string action name instead.
 */
export function getActionId(action) {
    invariant(typeof action === "function" || typeof action === "string", `useAction: action must be a function or string, got ${typeof action}`);
    const actionId = action?.$$id;
    if (actionId) {
        return normalizeActionId(actionId);
    }
    // If action is a string, use it directly
    if (typeof action === "string") {
        return action;
    }
    // If we get here, this is likely an action passed as prop from a server component
    // These lose their metadata during RSC serialization
    throw new Error(`useAction: Cannot extract action ID from function reference.

This typically happens when an action is passed as a prop from a server component.
Actions passed through RSC lose their metadata during serialization.

Solutions:
1. Import the action directly in your client component:
   import { myAction } from './actions';
   const state = useAction(myAction);

2. Use the action name as a string:
   const state = useAction("myAction");

The string must match the exported function name from your "use server" file.`);
}
export function useAction(action, selector) {
    const ctx = useContext(NavigationStoreContext);
    const actionId = typeof window !== "undefined" && typeof document !== "undefined"
        ? getActionId(action)
        : "";
    // Base state for useOptimistic
    const [baseState, setBaseState] = useState(() => {
        if (!ctx) {
            return selector ? selector(DEFAULT_ACTION_STATE) : DEFAULT_ACTION_STATE;
        }
        const state = ctx.eventController.getActionState(actionId);
        return selector ? selector(state) : state;
    });
    const prevSelected = useRef(baseState);
    prevSelected.current = baseState;
    // useOptimistic allows immediate updates during transitions/actions
    const [optimisticState, setOptimisticState] = useOptimistic(null);
    // Memoize the selector to avoid unnecessary re-subscriptions
    const selectorRef = useRef(selector);
    selectorRef.current = selector;
    // Subscribe to action state changes from event controller
    useEffect(() => {
        if (!ctx)
            return;
        // Subscribe to action-specific updates
        const unsubscribe = ctx.eventController.subscribeToAction(actionId, (state) => {
            const selectedState = selectorRef.current
                ? selectorRef.current(state)
                : state;
            if (!isShallowEqual(selectedState, prevSelected.current)) {
                prevSelected.current = selectedState;
                setBaseState(selectedState);
                startTransition(() => {
                    setOptimisticState(selectedState);
                });
            }
        });
        return () => {
            unsubscribe();
        };
    }, [actionId]);
    return (optimisticState ?? baseState);
}
function isShallowEqual(selectedState, baseState) {
    // If references are equal, they're shallow equal
    //@ts-expect-error -- TS doesn't like comparing generics
    if (selectedState === baseState) {
        return true;
    }
    // If either is null/undefined and they're not equal, they're not shallow equal
    if (selectedState == null || baseState == null) {
        return false;
    }
    // If types are different, they're not shallow equal
    if (typeof selectedState !== typeof baseState) {
        return false;
    }
    // For primitives, === comparison is sufficient (already checked above)
    if (typeof selectedState !== "object") {
        return false;
    }
    // For objects, compare keys and values shallowly
    const keysA = Object.keys(selectedState);
    const keysB = Object.keys(baseState);
    if (keysA.length !== keysB.length) {
        return false;
    }
    for (const key of keysA) {
        if (!Object.prototype.hasOwnProperty.call(baseState, key) ||
            selectedState[key] !== baseState[key]) {
            return false;
        }
    }
    return true;
}
//# sourceMappingURL=use-action.js.map