import { h as create, Fragment, h, VNode } from "preact";
import {
  ErrorAndLabel,
  FormErrors,
  FormModel,
  useForm,
} from "../hooks/useForm.js";
// import { getConverterById, useTranslationContext } from "../index.browser.js";
import { useState } from "preact/hooks";
import { useTranslationContext } from "../index.browser.js";
import {
  FieldComponentFunction,
  UIFormConfiguration,
  UIFormField,
} from "./field-types.js";
import {
  DoubleColumnFormSection,
  FormDesign,
  UIFormElementConfig,
} from "./forms-types.js";
import { convertFormConfigToUiField } from "./forms-utils.js";

export function DefaultForm<T>({
  design,
  initial,
  disabled,
}: {
  disabled?: boolean;
  design: FormDesign;
  initial: object;
}): VNode {
  const { model: handler, status } = useForm(design, initial);

  const [shorten, setShorten] = useState(true);

  return (
    <div>
      <hr class="mt-3 mb-3" />
      <FormUI design={design} model={handler} disabled={disabled} />
      <hr class="mt-3 mb-3" />
      <label>
        <input
          type="checkbox"
          checked={shorten}
          onChange={(e) => setShorten(!shorten)}
        />{" "}
        Shorten file contents.
      </label>

      <p>Result JSON:</p>
      <pre class="break-all whitespace-pre-wrap">
        {JSON.stringify(
          shorten
            ? redactFileContents(status.result ?? {})
            : status.result ?? {},
          undefined,
          2,
        )}
      </pre>
      <hr class="mt-3 mb-3" />
      {status.status !== "ok" ? (
        <ErrorsSummary errors={status.errors} />
      ) : undefined}
    </div>
  );
}

export function redactFileContents(result: any): any {
  if (Array.isArray(result)) {
    return result.map((x) => redactFileContents(x));
  }
  if (
    typeof result === "object" &&
    "ENCODING" in result &&
    result.ENCODING === "base64" &&
    "CONTENTS" in result
  ) {
    return {
      ...result,
      CONTENTS: "[... skipped ...]",
    };
  }
  if (typeof result === "object") {
    return Object.fromEntries(
      Object.entries(result).map(([k, v]) => {
        return [k, redactFileContents(v)];
      }),
    );
  }
  return result;
}

export const DEFAULT_FORM_UI_NAME = "form-ui";

/**
 * FIXME: formDesign should be embedded in formHandler
 */
export function FormUI<T>({
  name = DEFAULT_FORM_UI_NAME,
  design,
  model,
  disabled,
  focus,
  onSubmit,
}: {
  name?: string;
  design: FormDesign;
  model: FormModel;
  focus?: boolean;
  disabled?: boolean;
  onSubmit?: () => void;
}): VNode {
  switch (design.type) {
    case "double-column": {
      const ui = design.sections.map((section, i) => {
        if (!section) return <Fragment />;
        return (
          <DoubleColumnFormSectionUI
            sectionKey={`${i}`}
            key={i}
            name={name}
            section={section}
            model={model}
            focus={focus}
            disabled={disabled}
            onSubmit={onSubmit}
          />
        );
      });
      return (
        <Fragment>
          {design.title ? (
            <h1 class="text-lg font-bold">{design.title}</h1>
          ) : (
            <Fragment />
          )}
          {ui}
        </Fragment>
      );
    }
    case "single-column": {
      return (
        <SingleColumnFormSectionUI
          name={name}
          fields={design.fields}
          model={model}
          focus={focus}
          onSubmit={onSubmit}
          disabled={disabled}
        />
      );
    }
  }
}

export function DoubleColumnFormSectionUI<T>({
  sectionKey,
  section,
  name,
  focus,
  model,
  disabled,
  onSubmit,
}: {
  sectionKey: string;
  name: string;
  model: FormModel;
  section: DoubleColumnFormSection;
  focus?: boolean;
  disabled?: boolean;
  onSubmit?: () => void;
}): VNode {
  const { i18n } = useTranslationContext();
  const fs = convertFormConfigToUiField(
    i18n,
    sectionKey,
    section.fields,
    model,
  );
  const allHidden = fs.every((v) => {
    // FIXME: Handler should probably be present for all Form UI fields, not just some.
    if ("handler" in v.properties) {
      return v.properties.hidden ?? false;
    }
    return false;
  });
  const sectionHidden = model.isSectionHidden(sectionKey);
  if (allHidden || sectionHidden) {
    return (
      <RenderAllFieldsByUiConfig
        hidden={true}
        key="fields"
        fields={fs}
        focus={focus}
        disabled={disabled}
      />
    );
  }
  return (
    <form
      name={name}
      class="grid grid-cols-1 gap-x-8 gap-y-8 pt-5 md:grid-cols-3"
      onSubmit={(e) => {
        e.preventDefault();
        if (onSubmit) onSubmit();
      }}
    >
      <div class="px-4 sm:px-0">
        <h2 class="text-base font-semibold leading-7 text-gray-900">
          {section.title}
        </h2>
        {section.description && (
          <p class="mt-1 text-sm leading-6 text-gray-600">
            {section.description}
          </p>
        )}
      </div>
      <div class="bg-white shadow-sm ring-1 ring-gray-900/5 rounded-md md:col-span-2">
        <div class="p-3">
          <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
            <RenderAllFieldsByUiConfig
              key="fields"
              fields={fs}
              focus={focus}
              disabled={disabled}
            />
          </div>
        </div>
      </div>
    </form>
  );
}

export function SingleColumnFormSectionUI<T>({
  fields,
  name,
  model,
  focus,
  disabled,
  onSubmit,
}: {
  name: string;
  model: FormModel;
  fields: UIFormElementConfig[];
  focus?: boolean;
  disabled?: boolean;
  onSubmit?: () => void;
}): VNode {
  const { i18n } = useTranslationContext();
  return (
    <form
      name={name}
      class="bg-white shadow-sm ring-1 ring-gray-900/5 rounded-md md:col-span-2"
      onSubmit={(e) => {
        e.preventDefault();
        if (onSubmit) onSubmit();
      }}
    >
      <div class="p-3">
        <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
          <RenderAllFieldsByUiConfig
            fields={convertFormConfigToUiField(i18n, `root`, fields, model)}
            focus={focus}
            disabled={disabled}
          />
        </div>
      </div>
    </form>
  );
}

export function RenderAllFieldsByUiConfig({
  focus,
  fields,
  hidden,
  disabled,
}: {
  fields: UIFormField[];
  focus?: boolean;
  disabled?: boolean;
  hidden?: boolean;
}): VNode {
  return create(
    Fragment,
    {},
    fields.map((field, i) => {
      const Component = UIFormConfiguration[
        field.type
      ] as FieldComponentFunction<any>;
      const p: Record<string, any> = {
        ...field.properties,
        focus: !!focus && i === 0,
        hidden:
          hidden || ("hidden" in field.properties && field.properties.hidden),
      };
      if (disabled != null) {
        p.disabled = disabled;
      }

      return <Component key={i} {...p} />;
    }),
  );
}

export function ErrorsSummary<T>({
  errors,
  formName = DEFAULT_FORM_UI_NAME,
  startOpen,
  fixed,
}: {
  errors: FormErrors<T> | undefined;
  formName?: string;
  startOpen?: boolean;
  fixed?: boolean;
}): VNode {
  const { i18n } = useTranslationContext();
  const [opened, setOpened] = useState(startOpen ?? false);

  function Header() {
    return (
      <div
        data-fixed={!!fixed}
        class="p-2 relative bg-gray-200 flex justify-between data-[fixed=false]:cursor-pointer"
        onClick={() => {
          if (!fixed) {
            setOpened((o) => !o);
          }
        }}
      >
        <div class="px-4 sm:px-0">
          <h3 class="text-base/7 font-semibold text-gray-900">
            <i18n.Translate>Missing fields</i18n.Translate>
          </h3>
        </div>

        <div class="flex shrink-0 items-center gap-x-4">
          <div class="flex rounded-md shadow-sm border-0 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600"></div>
          {fixed ? (
            <Fragment />
          ) : (
            <div class="rounded-full bg-gray-50 p-2">
              <svg
                xmlns="http://www.w3.org/2000/svg"
                fill="none"
                viewBox="0 0 24 24"
                stroke-width="1.5"
                stroke="currentColor"
                class="size-4 h-4 w-4"
              >
                {opened ? (
                  <path
                    stroke-linecap="round"
                    stroke-linejoin="round"
                    d="m19.5 8.25-7.5 7.5-7.5-7.5"
                  />
                ) : (
                  <path
                    stroke-linecap="round"
                    stroke-linejoin="round"
                    d="m4.5 15.75 7.5-7.5 7.5 7.5"
                  />
                )}
              </svg>
            </div>
          )}
        </div>
      </div>
    );
  }
  if (!errors || !opened) {
    return (
      <div class="overflow-hidden border border-gray-800  rounded-xl">
        <Header />
      </div>
    );
  }

  return (
    <div class="overflow-hidden border border-gray-800 rounded-xl">
      <Header />

      <div class="border-t border-gray-100">
        <dl class="divide-y divide-gray-100">
          {Object.entries(errors).map(([fieldName, handler]) => {
            const errHandler = handler as ErrorAndLabel;
            //FIXME: don't rely on DOM to find the element
            // use preact REF
            return (
              <span
                href="#"
                onClick={(e) => {
                  e.preventDefault();
                  const el = document.querySelector(
                    `form[name=${formName}] label[for=${fieldName}]`,
                  ) as HTMLElement;
                  if (el) {
                    el.focus({ preventScroll: true });
                    el.scrollIntoView({
                      behavior: "smooth",
                      block: "center",
                      inline: "center",
                    });

                    el.classList.add("animate-pulse");
                    el.classList.add("border-b");
                    el.classList.add("border-red-700");
                    setTimeout(() => {
                      el.classList.remove("animate-pulse");
                      el.classList.remove("border-b");
                      el.classList.remove("border-red-700");
                    }, 5000);
                  }
                }}
                class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 odd:bg-white even:bg-gray-100 cursor-pointer"
              >
                {errHandler.section ? (
                  <dt class="underline pl-4 text-sm/6 font-medium text-gray-900">
                    {errHandler.section}: {errHandler.label}
                  </dt>
                ) : (
                  <dt class="underline pl-4 text-sm/6 font-medium text-gray-900">
                    {errHandler.label}
                  </dt>
                )}
                <dd class="underline flex text-sm/6 text-red-700 sm:col-span-2 sm:mt-0">
                  {errHandler.message}
                </dd>
              </span>
            );
          })}
        </dl>
      </div>
    </div>
  );
}
