import { Controller, type Path, type UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next";

import type { components } from "../api/schema";
import { useAuthorities, useTerms } from "../api/queries";
import { useConfig } from "../config/config-context";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";

type FieldDefinitionView = components["schemas"]["FieldDefinitionView"];
type LabelView = components["schemas"]["LabelView"];

type FieldForm<TValues extends { fields: Record<string, unknown> }> = UseFormReturn<TValues>;

function fieldPath<TValues extends { fields: Record<string, unknown> }>(
  key: string,
): Path<TValues> {
  return `fields.${key}` as Path<TValues>;
}

function labelIn(labels: LabelView[], lang: string): string {
  return (
    labels.find((l) => l.lang === lang)?.label ??
    labels.find((l) => l.lang === "en")?.label ??
    labels[0]?.label ??
    ""
  );
}

// A native <select> keeps the bundle lean and is fully accessible; the shadcn Select
// can replace it later without changing the value contract (option value = id).
function OptionsSelect({
  id,
  value,
  onChange,
  options,
  lang,
  placeholder,
}: {
  id: string;
  value: string;
  onChange: (v: string) => void;
  options: { id: string; labels: LabelView[] }[];
  lang: string;
  placeholder: string;
}) {
  return (
    <select
      id={id}
      className="w-full rounded border px-2 py-1 text-sm"
      value={value}
      onChange={(e) => onChange(e.target.value)}
    >
      <option value="">{placeholder}</option>

      {options.map((o) => (
        <option key={o.id} value={o.id}>
          {labelIn(o.labels, lang)}
        </option>
      ))}
    </select>
  );
}

export function FieldInput<TValues extends { fields: Record<string, unknown> }>({
  definition,
  form,
}: {
  definition: FieldDefinitionView;
  form: FieldForm<TValues>;
}) {
  const { t, i18n } = useTranslation();
  const { default_language } = useConfig();
  const lang = i18n.language.startsWith("sv") ? "sv" : "en";
  const label = labelIn(definition.labels, lang);
  const name = fieldPath<TValues>(definition.key);
  const placeholder = t("form.selectPlaceholder");

  switch (definition.data_type) {
    case "integer":
      return (
        <div className="space-y-1">
          <Label htmlFor={definition.key}>{label}</Label>

          <Input
            id={definition.key}
            type="number"
            {...form.register(name, {
              valueAsNumber: true,
              required: definition.required,
            })}
          />
        </div>
      );

    case "date":
      return (
        <div className="space-y-1">
          <Label htmlFor={definition.key}>{label}</Label>

          <Input
            id={definition.key}
            type="date"
            {...form.register(name, { required: definition.required })}
          />
        </div>
      );

    case "boolean":
      // A checkbox always has a boolean value, so `required` is a no-op here.
      return (
        <div className="flex items-center gap-2">
          <Controller
            control={form.control}
            name={name}
            render={({ field }) => (
              <Checkbox
                id={definition.key}
                checked={!!field.value}
                onCheckedChange={(checked) => field.onChange(checked === true)}
              />
            )}
          />

          <Label htmlFor={definition.key}>{label}</Label>
        </div>
      );

    case "localized_text":
      return (
        <div className="space-y-1">
          <Label htmlFor={definition.key}>{label}</Label>
          <Input
            id={definition.key}
            {...form.register(fieldPath<TValues>(`${definition.key}.${default_language}`), {
              required: definition.required,
            })}
          />
        </div>
      );

    case "term":
      return (
        <TermField
          definition={definition}
          form={form}
          label={label}
          lang={lang}
          placeholder={placeholder}
        />
      );

    case "authority":
      return (
        <AuthorityField
          definition={definition}
          form={form}
          label={label}
          lang={lang}
          placeholder={placeholder}
        />
      );

    case "text":
    default:
      return (
        <div className="space-y-1">
          <Label htmlFor={definition.key}>{label}</Label>

          <Input
            id={definition.key}
            {...form.register(name, { required: definition.required })}
          />
        </div>
      );
  }
}

function TermField<TValues extends { fields: Record<string, unknown> }>({
  definition,
  form,
  label,
  lang,
  placeholder,
}: {
  definition: FieldDefinitionView;
  form: FieldForm<TValues>;
  label: string;
  lang: string;
  placeholder: string;
}) {
  const { data: terms } = useTerms(definition.vocabulary_id);

  return (
    <div className="space-y-1">
      <Label htmlFor={definition.key}>{label}</Label>

      <Controller
        control={form.control}
        name={fieldPath<TValues>(definition.key)}
        rules={{ required: definition.required }}
        render={({ field }) => (
          <OptionsSelect
            id={definition.key}
            value={(field.value as string) ?? ""}
            onChange={field.onChange}
            options={terms ?? []}
            lang={lang}
            placeholder={placeholder}
          />
        )}
      />
    </div>
  );
}

function AuthorityField<TValues extends { fields: Record<string, unknown> }>({
  definition,
  form,
  label,
  lang,
  placeholder,
}: {
  definition: FieldDefinitionView;
  form: FieldForm<TValues>;
  label: string;
  lang: string;
  placeholder: string;
}) {
  const { data: authorities } = useAuthorities(definition.authority_kind);

  return (
    <div className="space-y-1">
      <Label htmlFor={definition.key}>{label}</Label>

      <Controller
        control={form.control}
        name={fieldPath<TValues>(definition.key)}
        rules={{ required: definition.required }}
        render={({ field }) => (
          <OptionsSelect
            id={definition.key}
            value={(field.value as string) ?? ""}
            onChange={field.onChange}
            options={authorities ?? []}
            lang={lang}
            placeholder={placeholder}
          />
        )}
      />
    </div>
  );
}
