import { ContextScope } from "@reins/utils";
import { ObjectType, PathsOf, ObjectPathToValue } from "@reins/types";

import {
  Form as FormComponent,
  FormProps,
  FormButton,
  FormButtonProps,
  FormPage,
  FormPageProps,
  FormController,
  FormControllerProps,
} from "components";
import { createUseField, UseField, UseFieldOptions } from "hooks";
import { FormAdapter, FormAdapterOptionsType } from "utils";
import { useActions } from "hooks/use-actions/use-actions";
import { useFormState } from "hooks/use-form-state/use-form-state";

export function createForm<Values extends ObjectType>(initialValues: Values, options?: FormAdapterOptionsType) {
  const form = new FormAdapter<Values, PathsOf<Values>>(initialValues, options);

  /* -----------------------------------------------------------------------------------------------
   * Context
   * ---------------------------------------------------------------------------------------------*/

  const formContexts = new ContextScope("Form").createContext("FormProvider", { form });

  const [FormProvider, useProviderContext] = formContexts.getContext("FormProvider");

  const Provider = (props: Omit<Parameters<typeof FormProvider.Provider>[0], "__context">) => (
    <FormProvider.Provider {...props} __context={formContexts} />
  );

  /* -----------------------------------------------------------------------------------------------
   * Form Hooks
   * ---------------------------------------------------------------------------------------------*/

  const useFormContext = (componentName: string) => {
    return useProviderContext(componentName, formContexts);
  };

  const useField = <N extends PathsOf<Values>>(
    name: N,
    args?: UseFieldOptions<FormAdapter<Values, PathsOf<Values>>, ObjectPathToValue<Values, N>>,
  ) => {
    return createUseField(useFormContext)<FormAdapter<Values, PathsOf<Values>>, ObjectPathToValue<Values, N>>(
      name,
      args,
    );
  };

  const useState = (...args: Parameters<(typeof form)["store"]["useStore"]>) => {
    return useFormState(form, ...args);
  };

  const useFormActions = () => {
    return useActions(form);
  };

  /* -----------------------------------------------------------------------------------------------
   * Form Components
   * ---------------------------------------------------------------------------------------------*/

  const Form = (props: FormProps<Values>) => <FormComponent {...props} formAdapter={form} Provider={Provider} />;

  const Button = (props: FormButtonProps) => <FormButton {...props} useFormContext={useFormContext} />;

  const Page = (props: FormPageProps) => <FormPage {...props} useFormContext={useFormContext} />;

  const Controller = <Name extends PathsOf<Values>, Value extends ObjectPathToValue<Values, Name>>(
    props: FormControllerProps<FormAdapter<Values, PathsOf<Values>>, Name, Value>,
  ) => (
    <FormController<FormAdapter<Values, PathsOf<Values>>, Name, Value>
      {...props}
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      useField={useField as UseField<FormAdapter<any, string>, any>}
    />
  );

  return {
    form,
    useActions: useFormActions,
    useFormState: useState,
    Form,
    FormButton: Button,
    FormPage: Page,
    FormController: Controller,
    useFormContext,
    useField,
  };
}
