import React, { createContext, useCallback, useContext, useEffect, useId, } from "react";
import { safeParse, } from "valibot";
import { useAtomState, useSetAtom } from "../../state";
import { atom, atomFamily } from "../../state/atom";
import { parseValue } from "../../utils/form";
/**
 * React Context that provides form metadata to child components.
 *
 * @internal
 */
export let FormContext = createContext({
    formId: "",
});
/**
 * Hook to access the current form's context.
 *
 * @returns The form context containing the schema and form ID.
 *
 * @example
 * ```tsx
 * function MyInput() {
 *   const { formId, schema } = useFormContext();
 *   // ...
 * }
 * ```
 */
export function useFormContext() {
    return useContext(FormContext);
}
/**
 * A type-safe Form component that integrates with Valibot for schema validation.
 *
 * @remarks
 * This component handles form submission, data parsing, validation, and error reporting.
 * It automatically manages the form's submission state (pending/submitted) via a global atom family.
 *
 * Key features:
 * - **Automatic Validation**: Validates all fields against the provided `schema` on submit.
 * - **Type Safety**: The `onSubmit` handler receives strongly-typed data inferred from the schema.
 * - **Error Handling**: Focuses the first invalid field and sets custom validity messages using the browser's Constraint Validation API.
 * - **State Management**: Tracks submission status (`isPending`, `isSubmitted`) which can be accessed via {@link useFormStatus}.
 * - **Radio/Checkbox Handling**: Correctly parses `RadioNodeList` and checkbox groups into arrays or single values.
 *
 * @param props - The component props.
 * @returns The rendered form element wrapped in a context provider.
 *
 * @example
 * ```tsx
 * import { object, string, email } from 'valibot';
 * import { Form } from '@packages/werkbank/component/form';
 *
 * const schema = object({
 *   email: string([email()]),
 *   password: string(),
 * });
 *
 * function LoginForm() {
 *   return (
 *     <Form
 *       schema={schema}
 *       onSubmit={(data) => {
 *         // data is typed as { email: string; password: string }
 *         console.log(data);
 *       }}
 *     >
 *       <input name="email" type="email" />
 *       <input name="password" type="password" />
 *       <button type="submit">Login</button>
 *     </Form>
 *   );
 * }
 * ```
 */
export function Form({ schema, onSubmit, onSchemaIssues, children, id, ...props }) {
    let formId = useId();
    let formAction = useSetAtom(formFamily(formId));
    useEffect(() => () => {
        formFamily.delete(formId);
    }, [formId]);
    let handleSubmit = useCallback((event) => {
        event.preventDefault();
        if (!onSubmit) {
            return;
        }
        let form = event.currentTarget;
        let elements = form.elements;
        let data = {};
        // Collect unique names to iterate over
        let names = new Set();
        for (let i = 0; i < elements.length; i++) {
            let elm = elements[i];
            if (elm.name) {
                names.add(elm.name);
            }
        }
        for (let name of names) {
            let item = elements.namedItem(name);
            if (!item)
                continue;
            if (item instanceof RadioNodeList) {
                // Handle RadioNodeList (Radios or Multi-checkboxes)
                // For radios, .value gives the checked value.
                // For checkboxes with same name, we might need to collect all checked ones.
                // But RadioNodeList.value returns the value of the first checked radio button.
                // If it's checkboxes, we need to iterate.
                let values = [];
                let isCheckbox = false;
                for (let i = 0; i < item.length; i++) {
                    let node = item[i];
                    if (node.type === "checkbox") {
                        isCheckbox = true;
                        if (node.checked) {
                            values.push(parseValue(node, node.value));
                        }
                    }
                }
                if (isCheckbox) {
                    data[name] = values;
                }
                else {
                    // Radio buttons
                    data[name] = item.value;
                }
            }
            else if (item instanceof HTMLInputElement &&
                item.type === "checkbox") {
                // Single checkbox
                data[name] = item.checked;
            }
            else {
                // Single element
                let elm = item;
                data[name] = parseValue(elm, elm.value);
            }
        }
        let result = safeParse(schema, data);
        if (result.success) {
            formAction.pending(true);
            Promise.resolve(onSubmit(result.output)).finally(() => {
                formAction.pending(false);
            });
            return;
        }
        formAction.submitted(true);
        // Find first invalid element and report validity
        let firstIssue = result.issues.at(0);
        if (firstIssue?.path[0]) {
            let key = firstIssue.path[0].key;
            let elm = form.elements.namedItem(key);
            if (elm) {
                let target = (elm instanceof RadioNodeList ? elm[0] : elm);
                target.setCustomValidity?.(firstIssue.message);
                target.reportValidity?.();
            }
        }
        onSchemaIssues?.(result.issues);
    }, [onSubmit, onSchemaIssues, schema, formAction]);
    return (<FormContext.Provider value={{ schema, formId }}>
			<form id={id} {...props} onSubmit={handleSubmit}>
				{children}
			</form>
		</FormContext.Provider>);
}
/**
 * An atom family that stores the state for each form instance.
 *
 * @internal
 */
export let formFamily = atomFamily((_id) => {
    return atom({
        initialState: {
            isSubmitted: false,
            isPending: false,
        },
        reducers: {
            submitted: (state, value) => {
                state.isSubmitted = value;
            },
            pending: (state, value) => {
                state.isPending = value;
            },
        },
    });
});
/**
 * Hook to access the status of the nearest parent {@link Form}.
 *
 * @returns The current form state (`isSubmitted`, `isPending`).
 *
 * @example
 * ```tsx
 * function SubmitButton() {
 *   const { isPending } = useFormStatus();
 *   return <button disabled={isPending}>Submit</button>;
 * }
 * ```
 */
export function useFormStatus() {
    let { formId } = useFormContext();
    return useAtomState(formFamily(formId));
}
