import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";

import { useFieldDefinitions } from "../api/queries";
import { useConfig } from "../config/config-context";
import { FieldInput } from "./field-input";
import { pruneFields } from "./prune-fields";
import { groupDefinitions } from "../lib/group-fields";
import { UnsavedChangesDialog } from "../lib/unsaved-changes-dialog";
import { useUnsavedChanges } from "../lib/use-unsaved-changes";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";

export type ObjectCore = {
  object_number: string;
  object_name: string;
  number_of_objects: number;
  brief_description: string | null;
  current_location: string | null;
  current_owner: string | null;
  recorder: string | null;
  recording_date: string | null;
};

export type ObjectFormValues = {
  core: ObjectCore;
  visibility?: "draft" | "internal";
  fields: Record<string, unknown>;
};

type FormShape = {
  core: ObjectCore;
  visibility: "draft" | "internal";
  fields: Record<string, unknown>;
} & Record<string, unknown>;

const EMPTY_CORE: ObjectCore = {
  object_number: "",
  object_name: "",
  number_of_objects: 1,
  brief_description: null,
  current_location: null,
  current_owner: null,
  recorder: null,
  recording_date: null,
};

export function ObjectForm({
  mode,
  defaults,
  onSubmit,
  onCancel,
  formError,
  fieldErrorKey,
  fieldErrorCode,
}: {
  mode: "create" | "edit";
  defaults?: { core: ObjectCore; fields: Record<string, unknown> };
  onSubmit: (values: ObjectFormValues, opts?: { createAnother?: boolean }) => Promise<boolean> | boolean;
  onCancel: () => void;
  formError?: string | null;
  fieldErrorKey?: string | null;
  fieldErrorCode?: string | null;
}) {
  const { t } = useTranslation();
  const { default_language } = useConfig();

  const { data: definitions } = useFieldDefinitions();

  const localizedTextKeys = new Set(
    (definitions ?? []).filter((d) => d.data_type === "localized_text").map((d) => d.key),
  );

  const form = useForm<FormShape>({
    defaultValues: {
      core: defaults?.core ?? EMPTY_CORE,
      visibility: "draft",
      fields: defaults?.fields ?? {},
    },
  });

  const { register, handleSubmit, formState: { errors, isSubmitting, isDirty } } = form;

  const blocker = useUnsavedChanges(isDirty && !isSubmitting);

  useEffect(() => {
    if (fieldErrorKey) {
      const codeKey = fieldErrorCode ? `form.fieldError.${fieldErrorCode}` : "";
      const message =
        fieldErrorCode && t(codeKey) !== codeKey
          ? t(codeKey)
          : t("form.fieldRejected", { field: fieldErrorKey });
      form.setError(`fields.${fieldErrorKey}` as never, { type: "server", message });
    }
  }, [fieldErrorKey, fieldErrorCode, form, t]);

  const runSubmit = (createAnother: boolean) =>
    handleSubmit(async (data) => {
      const fields = pruneFields(data.fields, localizedTextKeys, default_language);
      const values =
        mode === "create"
          ? { core: data.core, visibility: data.visibility, fields }
          : { core: data.core, fields };
      const ok = await onSubmit(values, { createAnother });
      if (ok && createAnother) {
        form.reset({ core: EMPTY_CORE, visibility: "draft", fields: {} });
        document.getElementById("object_number")?.focus();
      }
    });

  const submit = runSubmit(false);

  const coreField = (
    key: keyof ObjectCore,
    labelKey: string,
    opts?: { type?: string; required?: boolean; min?: number },
  ) => (
    <div className="space-y-1">
      <Label htmlFor={key}>{t(`fieldsLabels.${labelKey}`)}</Label>

      <Input
        id={key}
        type={opts?.type ?? "text"}
        {...register(
          `core.${key}` as const,
          opts?.type === "number"
            ? {
                valueAsNumber: true,
                required: opts?.required,
                ...(opts?.min !== undefined
                  ? { min: { value: opts.min, message: t("form.minCount") } }
                  : {}),
              }
            : { required: opts?.required },
        )}
      />

      {errors.core?.[key] && (
        <p role="alert" className="text-xs text-destructive">
          {errors.core[key]?.message || t("form.required")}
        </p>
      )}
    </div>
  );

  return (
    <form
      onSubmit={submit}
      onKeyDown={(e) => {
        if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
          e.preventDefault();
          void submit();
        }
      }}
      className="space-y-4 overflow-auto p-4"
    >
      <UnsavedChangesDialog blocker={blocker} />

      {formError && (
        <p role="alert" className="text-sm text-destructive">
          {formError}
        </p>
      )}

      {coreField("object_number", "objectNumber", { required: true })}
      {coreField("object_name", "objectName", { required: true })}
      {coreField("number_of_objects", "count", { type: "number", required: true, min: 1 })}
      {coreField("brief_description", "briefDescription")}
      {coreField("current_location", "currentLocation")}
      {coreField("current_owner", "currentOwner")}
      {coreField("recorder", "recorder")}
      {coreField("recording_date", "recordingDate", { type: "date" })}

      {mode === "create" && (
        <div className="space-y-1">
          <Label htmlFor="visibility">{t("form.visibility")}</Label>

          <Controller
            control={form.control}
            name="visibility"
            render={({ field }) => (
              <Select value={field.value} onValueChange={field.onChange}>
                <SelectTrigger id="visibility">
                  <SelectValue />
                </SelectTrigger>
                <SelectContent>
                  <SelectItem value="draft">{t("form.draft")}</SelectItem>
                  <SelectItem value="internal">{t("form.internal")}</SelectItem>
                </SelectContent>
              </Select>
            )}
          />
        </div>
      )}

      {definitions && definitions.length > 0 && (
        <fieldset className="space-y-3 border-t pt-3">
          <legend className="sr-only">{t("form.flexibleHeading")}</legend>

          {groupDefinitions(definitions, t("fields.other")).map((g) => (
            <div key={g.group} className="space-y-3">
              <div className="label-caption">{g.group}</div>

              {g.defs.map((def) => (
                <div key={def.key}>
                  <FieldInput definition={def} form={form} />

                  {errors.fields?.[def.key] && (
                    <p role="alert" className="text-xs text-destructive">
                      {errors.fields[def.key]?.message ?? t("form.required")}
                    </p>
                  )}
                </div>
              ))}
            </div>
          ))}
        </fieldset>
      )}

      <div className="flex gap-2 pt-2">
        <Button type="submit" disabled={isSubmitting}>
          {isSubmitting ? t("form.saving") : mode === "create" ? t("form.create") : t("form.save")}
        </Button>

        {mode === "create" && (
          <Button
            type="button"
            variant="secondary"
            disabled={isSubmitting}
            onClick={() => void runSubmit(true)()}
          >
            {t("form.createAnother")}
          </Button>
        )}

        <Button type="button" variant="ghost" disabled={isSubmitting} onClick={onCancel}>
          {t("form.cancel")}
        </Button>
      </div>
    </form>
  );
}

