"use client";

import { useId, type ReactNode, type SelectHTMLAttributes, type InputHTMLAttributes, type TextareaHTMLAttributes } from "react";

// Form primitives.
//
// Before these existed the same class chain was written out by hand at 85
// call sites, each with a bare `<label>` next to a bare `<input>` and no
// connection between them — 92 labels, 4 of which used htmlFor, and not a
// single input carried an id. Screen readers announced an unnamed edit box
// and clicking a label focused nothing. Generating the id here makes that
// impossible to get wrong, and gives every field one place to fix focus
// styling, error display and sizing.

export const CONTROL_CLASS =
  "w-full rounded border border-border bg-white px-3 py-2 text-sm text-ink " +
  "focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-brand-500 " +
  "disabled:cursor-not-allowed disabled:bg-surface disabled:text-ink-muted";

const INVALID_CLASS = "border-danger-solid";

/** Auto-width select for filter bars, where the label is an aria-label. */
export const FILTER_SELECT_CLASS =
  "rounded border border-border bg-white px-3 py-2 text-sm text-ink " +
  "focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-brand-500";

type FieldChildProps = {
  id: string;
  "aria-describedby": string | undefined;
  "aria-invalid": true | undefined;
};

type FieldShellProps = {
  label: string;
  /** Marks the label and sets required on the control. */
  required?: boolean;
  /** Helper text below the control; announced with the field. */
  hint?: string;
  /** Replaces the hint when set and marks the control invalid. */
  error?: string | null;
  /** Smaller label, used inside dense side panels. */
  dense?: boolean;
  className?: string;
};

/**
 * Escape hatch for controls this module does not wrap (Lookup, CountryPicker,
 * Picklist). Hands the wiring to the caller instead of guessing at it:
 *
 *   <Field label="Land">{(p) => <CountryPicker {...p} … />}</Field>
 */
export function Field({
  label,
  required,
  hint,
  error,
  dense,
  className,
  children,
}: FieldShellProps & { children: (props: FieldChildProps) => ReactNode }) {
  const id = useId();
  const messageId = `${id}-message`;
  const message = error ?? hint;

  return (
    <div className={className}>
      <label htmlFor={id} className={dense ? "mb-1 block text-xs font-semibold text-ink-muted" : "mb-1 block text-sm font-semibold text-ink"}>
        {label}
        {required && <span aria-hidden> *</span>}
        {required && <span className="sr-only"> (Pflichtfeld)</span>}
      </label>
      {children({
        id,
        "aria-describedby": message ? messageId : undefined,
        "aria-invalid": error ? true : undefined,
      })}
      {message && (
        <p id={messageId} className={`mt-1 text-xs ${error ? "font-semibold text-danger-text" : "text-ink-muted"}`}>
          {message}
        </p>
      )}
    </div>
  );
}

type TextFieldProps = FieldShellProps &
  Omit<InputHTMLAttributes<HTMLInputElement>, "onChange" | "id" | "className"> & {
    value: string;
    /** Receives the value directly — every call site wanted e.target.value. */
    onChange: (value: string) => void;
  };

export function TextField({ label, required, hint, error, dense, className, value, onChange, ...rest }: TextFieldProps) {
  return (
    <Field label={label} required={required} hint={hint} error={error} dense={dense} className={className}>
      {(p) => (
        <input
          {...p}
          {...rest}
          required={required}
          value={value}
          onChange={(e) => onChange(e.target.value)}
          className={`${CONTROL_CLASS} ${error ? INVALID_CLASS : ""}`}
        />
      )}
    </Field>
  );
}

type Option = { value: string; label: string; disabled?: boolean };

type SelectFieldProps = FieldShellProps &
  Omit<SelectHTMLAttributes<HTMLSelectElement>, "onChange" | "id" | "className" | "children"> & {
    value: string;
    onChange: (value: string) => void;
    options: readonly Option[];
    /** Prepends a disabled placeholder, for "Bitte wählen…" selects. */
    placeholder?: string;
  };

export function SelectField({
  label,
  required,
  hint,
  error,
  dense,
  className,
  value,
  onChange,
  options,
  placeholder,
  ...rest
}: SelectFieldProps) {
  return (
    <Field label={label} required={required} hint={hint} error={error} dense={dense} className={className}>
      {(p) => (
        <select
          {...p}
          {...rest}
          required={required}
          value={value}
          onChange={(e) => onChange(e.target.value)}
          className={`${CONTROL_CLASS} ${error ? INVALID_CLASS : ""}`}
        >
          {placeholder && (
            <option value="" disabled>
              {placeholder}
            </option>
          )}
          {options.map((o) => (
            <option key={o.value} value={o.value} disabled={o.disabled}>
              {o.label}
            </option>
          ))}
        </select>
      )}
    </Field>
  );
}

type TextareaFieldProps = FieldShellProps &
  Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, "onChange" | "id" | "className"> & {
    value: string;
    onChange: (value: string) => void;
  };

export function TextareaField({ label, required, hint, error, dense, className, value, onChange, ...rest }: TextareaFieldProps) {
  return (
    <Field label={label} required={required} hint={hint} error={error} dense={dense} className={className}>
      {(p) => (
        <textarea
          {...p}
          {...rest}
          required={required}
          value={value}
          onChange={(e) => onChange(e.target.value)}
          className={`${CONTROL_CLASS} ${error ? INVALID_CLASS : ""}`}
        />
      )}
    </Field>
  );
}
