import {
  cloneElement,
  isValidElement,
  useId,
  type ReactElement,
  type ReactNode,
} from "react";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch";

/** Labeled field wrapper: label (+required) + the input + an error/hint line. */
export function Field({
  label,
  required,
  error,
  hint,
  children,
}: {
  label: string;
  required?: boolean;
  error?: string;
  hint?: string;
  children: ReactNode;
}) {
  // Associate the <label> with the control. Generate a stable id, render it via
  // `htmlFor`, and inject the same id onto the single input child so click-to-
  // focus works and screen readers announce the label. Wire error/hint via
  // aria-describedby + aria-invalid for assistive tech. We only inject onto a
  // valid element that hasn't already set `id`, so explicit caller ids win.
  const reactId = useId();
  const generatedId = `field-${reactId}`;
  // If the child already carries an id, keep it as the htmlFor target so the
  // label still points at the real input; otherwise inject the generated one.
  const childId = isValidElement(children)
    ? (children.props as { id?: string }).id
    : undefined;
  const inputId = childId ?? generatedId;
  const describedById = error
    ? `${inputId}-error`
    : hint
      ? `${inputId}-hint`
      : undefined;

  let control = children;
  if (isValidElement(children)) {
    const extra: Record<string, unknown> = {};
    if (childId == null) extra.id = inputId;
    if (describedById) extra["aria-describedby"] = describedById;
    if (error) extra["aria-invalid"] = true;
    if (Object.keys(extra).length > 0) {
      control = cloneElement(
        children as ReactElement<Record<string, unknown>>,
        extra,
      );
    }
  }

  return (
    <Box sx={{ mb: 2 }}>
      <Typography
        component="label"
        htmlFor={inputId}
        variant="body2"
        sx={{
          display: "block",
          mb: 0.5,
          fontWeight: 600,
          color: "text.secondary",
        }}
      >
        {label}
        {required && (
          <Box component="span" sx={{ color: "error.main", ml: 0.25 }}>
            *
          </Box>
        )}
      </Typography>
      {control}
      {error ? (
        <Typography id={describedById} variant="caption" color="error">
          {error}
        </Typography>
      ) : hint ? (
        <Typography id={describedById} variant="caption" color="text.secondary">
          {hint}
        </Typography>
      ) : null}
    </Box>
  );
}

/** Boolean switch with a label. */
export function SwitchField({
  label,
  checked,
  onChange,
  disabled,
}: {
  label: string;
  checked: boolean;
  onChange: (v: boolean) => void;
  disabled?: boolean;
}) {
  return (
    <FormControlLabel
      control={
        <Switch
          checked={checked}
          disabled={disabled}
          onChange={(e) => onChange(e.target.checked)}
        />
      }
      label={label}
      disabled={disabled}
    />
  );
}
