import type { PrimaryFieldType, Entity, EntityData, Field } from "bknd";
import type { FieldApi, ReactFormExtendedApi } from "@tanstack/react-form";
import type { JSX } from "react";
import { useStore } from "@tanstack/react-store";
import { MediaField } from "media/MediaField";
import { type ComponentProps, Suspense, useState } from "react";
import { JsonEditor } from "ui/components/code/JsonEditor";
import * as Formy from "ui/components/form/Formy";
import { FieldLabel } from "ui/components/form/Formy";
import { type FileState, Media } from "ui/elements";
import { useEvent } from "ui/hooks/use-event";
import { EntityJsonSchemaFormField } from "./fields/EntityJsonSchemaFormField";
import { EntityRelationalFormField } from "./fields/EntityRelationalFormField";
import ErrorBoundary from "ui/components/display/ErrorBoundary";
import { Alert } from "ui/components/display/Alert";
import { bkndModals } from "ui/modals";
import type { EnumField, JsonField, JsonSchemaField } from "data/fields";
import type { RelationField } from "data/relations";
import { useEntityAdminOptions } from "ui/options";

// simplify react form types 🤦
// biome-ignore format: ...
export type FormApi = ReactFormExtendedApi<any, any, any, any, any, any, any, any, any, any, any, any>;
// biome-ignore format: ...
export type TFieldApi = FieldApi<any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any>;

type EntityFormProps = {
   entity: Entity;
   entityId?: PrimaryFieldType;
   data?: EntityData;
   handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
   fieldsDisabled: boolean;
   Form: FormApi;
   className?: string;
   action: "create" | "update";
};

export function EntityForm({
   entity,
   entityId,
   handleSubmit,
   fieldsDisabled,
   Form,
   data,
   className,
   action,
}: EntityFormProps) {
   const fields = entity.getFillableFields(action, true);
   const options = useEntityAdminOptions(entity, action);

   return (
      <form onSubmit={handleSubmit}>
         <Form.Subscribe
            selector={(state) => {
               //console.log("state", state);
               return [state.canSubmit, state.isValid, state.errors];
            }}
            children={([canSubmit, isValid, errors]) => {
               //console.log("form:state", { canSubmit, isValid, errors });
               return (
                  !isValid && (
                     <div className="flex flex-col dark:bg-red-950 bg-red-100 p-4">
                        <p>Form is invalid.</p>
                        {Array.isArray(errors) && (
                           <ul className="list-disc">
                              {errors.map((error, key) => (
                                 <li className="ml-6" key={key}>
                                    {error}
                                 </li>
                              ))}
                           </ul>
                        )}
                     </div>
                  )
               );
            }}
         />
         <div className={className}>
            {fields.map((field, key) => {
               // @todo: tanstack form re-uses the state, causes issues navigating between entities w/ same fields

               // media field needs to render outside of the form
               // as its value is not stored in the form state
               if (field instanceof MediaField) {
                  return (
                     <EntityMediaFormField
                        key={field.name + key}
                        entity={entity}
                        entityId={entityId}
                        formApi={Form}
                        field={field}
                     />
                  );
               }

               if (!field.isFillable(action)) {
                  return;
               }

               const _key = `${entity.name}-${field.name}-${key}`;

               return (
                  <ErrorBoundary
                     key={_key}
                     fallback={
                        <Alert.Exception className="font-mono">
                           Field error: {field.name}
                        </Alert.Exception>
                     }
                  >
                     <Form.Field
                        name={field.name}
                        children={(props) => {
                           const fieldOptions = options.field(field.name);
                           if (fieldOptions?.render) {
                              const custom = fieldOptions.render(action, entity, field, {
                                 handleChange: props.handleChange,
                                 value: props.state.value,
                                 data,
                              });
                              if (custom) {
                                 return custom;
                              }
                           }
                           if (field.isHidden(action)) return;

                           return (
                              <EntityFormField
                                 field={field}
                                 fieldApi={props}
                                 disabled={fieldsDisabled}
                                 tabIndex={key + 1}
                                 action={action}
                                 data={data}
                              />
                           );
                        }}
                     />
                  </ErrorBoundary>
               );
            })}
         </div>
         <div className="hidden">
            <button type="submit" />
         </div>
      </form>
   );
}

type EntityFormFieldProps<
   T extends keyof JSX.IntrinsicElements = "input",
   F extends Field = Field,
> = ComponentProps<T> & {
   fieldApi: TFieldApi;
   field: F;
   action: "create" | "update";
   data?: EntityData;
};

type FormInputElement = HTMLInputElement | HTMLTextAreaElement;

function EntityFormField({ fieldApi, field, action, data, ...props }: EntityFormFieldProps) {
   const handleUpdate = useEvent((e: React.ChangeEvent<FormInputElement> | any) => {
      if (typeof e === "object" && "target" in e) {
         //console.log("handleUpdate", e.target.value);
         fieldApi.handleChange(e.target.value);
      } else {
         //console.log("handleUpdate-", e);
         fieldApi.handleChange(e);
      }
   });

   //const required = field.isRequired();
   //const customFieldProps = { ...props, action, required };

   if (field.type === "relation") {
      return (
         <EntityRelationalFormField
            fieldApi={fieldApi}
            field={field as RelationField}
            data={data}
            disabled={props.disabled}
            tabIndex={props.tabIndex}
         />
      );
   }

   if (field.type === "json") {
      return <EntityJsonFormField fieldApi={fieldApi} field={field as JsonField} {...props} />;
   }

   if (field.type === "jsonschema") {
      return (
         <EntityJsonSchemaFormField
            fieldApi={fieldApi}
            field={field as JsonSchemaField}
            data={data}
            disabled={props.disabled}
            tabIndex={props.tabIndex}
            {...props}
         />
      );
   }

   if (field.type === "enum") {
      return <EntityEnumFormField fieldApi={fieldApi} field={field as EnumField} {...props} />;
   }

   const fieldElement = field.getHtmlConfig().element;
   const fieldProps = field.getHtmlConfig().props as any;
   const Element = Formy.formElementFactory(fieldElement ?? "input", fieldProps);

   return (
      <Formy.Group>
         <FieldLabel htmlFor={fieldApi.name} field={field} />
         <Element
            {...fieldProps}
            name={fieldApi.name}
            id={fieldApi.name}
            value={fieldApi.state.value}
            onBlur={fieldApi.handleBlur}
            onChange={handleUpdate}
            required={field.isRequired()}
            {...props}
         />
      </Formy.Group>
   );
}

function EntityMediaFormField({
   formApi,
   field,
   entity,
   entityId,
   disabled,
}: {
   formApi: FormApi;
   field: MediaField;
   entity: Entity;
   entityId?: PrimaryFieldType;
   disabled?: boolean;
}) {
   if (!entityId) return;
   const maxLimit = 50;
   const maxItems = field.getMaxItems();
   const isSingle = maxItems === 1;
   const limit = isSingle ? 1 : maxItems && maxItems > maxLimit ? maxLimit : maxItems;

   const value = useStore(formApi.store, (state) => {
      const val = state.values[field.name];
      if (!val || typeof val === "undefined") return [];
      if (Array.isArray(val)) return val;
      return [val];
   });

   const key = JSON.stringify([entity, entityId, field.name, value.length]);
   const onClick = (file: FileState) => {
      bkndModals.open(bkndModals.ids.mediaInfo, {
         file,
      });
   };

   return (
      <Formy.Group>
         <FieldLabel field={field} />
         <Media.Dropzone
            key={key}
            maxItems={maxItems}
            allowedMimeTypes={field.getAllowedMimeTypes()}
            initialItems={isSingle ? value : undefined}
            onClick={onClick}
            entity={{
               name: entity.name,
               id: entityId,
               field: field.name,
            }}
            query={{
               sort: "-id",
               limit,
            }}
         />
      </Formy.Group>
   );
}

function EntityJsonFormField({
   fieldApi,
   field,
   ...props
}: { fieldApi: TFieldApi; field: JsonField }) {
   const [error, setError] = useState<any>(null);
   const handleUpdate = useEvent((value: any) => {
      setError(null);
      fieldApi.handleChange(value);
   });

   return (
      <Formy.Group error={!!error}>
         <Formy.FieldLabel htmlFor={fieldApi.name} field={field} />
         <Suspense>
            <JsonEditor
               id={fieldApi.name}
               value={fieldApi.state.value}
               onChange={handleUpdate}
               onBlur={fieldApi.handleBlur}
               onInvalid={setError}
               minHeight="100"
               {...props}
            />
         </Suspense>
      </Formy.Group>
   );
}

function EntityEnumFormField({
   fieldApi,
   field,
   ...props
}: { fieldApi: TFieldApi; field: EnumField }) {
   const handleUpdate = useEvent((e: React.ChangeEvent<HTMLTextAreaElement>) => {
      fieldApi.handleChange(e.target.value);
   });

   return (
      <Formy.Group>
         <Formy.FieldLabel htmlFor={fieldApi.name} field={field} />
         <Formy.Select
            name={fieldApi.name}
            id={fieldApi.name}
            value={fieldApi.state.value}
            onBlur={fieldApi.handleBlur}
            onChange={handleUpdate as any}
            required={field.isRequired()}
            {...props}
         >
            {!field.isRequired() && <option value="">- Select -</option>}
            {field.getOptions().map((option, i) => (
               <option key={`${option.value}-${i}`} value={option.value}>
                  {option.label}
               </option>
            ))}
         </Formy.Select>
      </Formy.Group>
   );
}
