import { SvelteComponentTyped } from "svelte";
import type { SvelteHTMLElements } from "svelte/elements";

type $RestProps = SvelteHTMLElements["div"];

type $Props = {
  /**
   * Specify the number of input segments to render
   * @default 4
   */
  count?: number;

  /**
   * The concatenated code value.
   *
   * Derived from `code` (i.e. `code.join("")`); bind to read the assembled
   * value. When `complete` is `true`, its length equals `count`. Each
   * character matches the active `type`: `0-9` for `"numeric"`, `a-zA-Z0-9`
   * for `"alphanumeric"`. Original casing is preserved regardless of
   * `uppercase`, which only affects the visual rendering.
   * @default ""
   */
  value?: string;

  /**
   * The individual segment characters.
   *
   * `code` is the source of truth; its length tracks `count`. Each element is
   * either an empty string (unfilled segment) or a single character matching
   * the active `type`: `0-9` for `"numeric"`, `a-zA-Z0-9` for
   * `"alphanumeric"`.
   * @default []
   */
  code?: string[];

  /**
   * Specify the type of allowed characters.
   *
   * `"numeric"` allows `0-9`; `"alphanumeric"` allows `a-z`, `A-Z`, `0-9`.
   * @default "numeric"
   */
  type?: "numeric" | "alphanumeric";

  /**
   * Set to `true` to visually display the characters in uppercase
   * while retaining the original casing of `value` and `code`.
   *
   * Only affects letters, so it is most useful with `type="alphanumeric"`.
   * @default false
   */
  uppercase?: boolean;

  /**
   * Set to `true` to mask the segments.
   *
   * Each segment is masked when it is not focused, revealing its character
   * only while focused.
   * @default false
   */
  mask?: boolean;

  /**
   * `true` when every segment is filled.
   * @default false
   */
  complete?: boolean;

  /**
   * Set to `true` to disable the input
   * @default false
   */
  disabled?: boolean;

  /**
   * Set the size of the input.
   * @default "default"
   */
  size?: "default" | "xs" | "sm" | "xl";

  /**
   * Set to `true` to enable the light variant
   * @default false
   */
  light?: boolean;

  /**
   * Specify the label text
   * @default ""
   */
  labelText?: string;

  /**
   * Set to `true` to visually hide the label text
   * @default false
   */
  hideLabel?: boolean;

  /**
   * Specify the helper text
   * @default ""
   */
  helperText?: string;

  /**
   * Placeholder shown in empty segments.
   *
   * Defaults to a placeholder in the fluid variant; non-fluid segments have no
   * placeholder unless this prop is set.
   * @default undefined
   */
  placeholder?: string | undefined;

  /**
   * Set to `true` to indicate an invalid state
   * @default false
   */
  invalid?: boolean;

  /**
   * Specify the invalid state text
   * @default ""
   */
  invalidText?: string;

  /**
   * Set to `true` to indicate a warning state
   * @default false
   */
  warn?: boolean;

  /**
   * Specify the warning state text
   * @default ""
   */
  warnText?: string;

  /**
   * Set to `true` to mark the field as required
   * @default false
   */
  required?: boolean;

  /**
   * Set to `true` to use the read-only variant
   * @default false
   */
  readonly?: boolean;

  /**
   * Set to `true` to use the fluid variant.
   * Inherited from the parent `FluidForm` context,
   * so it does not need to be set when used inside `FluidForm`.
   * @default false
   */
  fluid?: boolean;

  /**
   * Set to `true` to select a segment's value when it receives focus,
   * including on click. Without this prop, browsers typically select the
   * value only on keyboard focus (for example, via Tab).
   * @default false
   */
  selectTextOnFocus?: boolean;

  /**
   * Set an id for the input group
   * @default `ccs-${Math.random().toString(36)}`
   */
  id?: string;

  /**
   * Obtain a reference to the outer element.
   * @default null
   */
  ref?: null | HTMLDivElement;

  labelChildren?: (this: void) => void;

  [key: `data-${string}`]: unknown;
};

export type PinCodeInputProps = Omit<$RestProps, keyof $Props> & $Props;

export default class PinCodeInput extends SvelteComponentTyped<
  PinCodeInputProps,
  {
    change: CustomEvent<{
        value: string;
        code: string[]
      }>;
    clear: CustomEvent<null>;
    complete: CustomEvent<{
        value: string;
        code: string[]
      }>;
    paste: CustomEvent<{ value: string }>;
  },
  { labelChildren: Record<string, never> }
> {
  /**
   * Focus the first input segment.
   */
  focusFirstInput: (options?: { selectTextOnFocus?: boolean }) => any;

  /**
   * Focus the last input segment.
   */
  focusLastInput: (options?: { selectTextOnFocus?: boolean }) => any;

  /**
   * Focus the first empty input segment, or the last if all are filled.
   */
  focusNextEmptyInput: (options?: { selectTextOnFocus?: boolean }) => any;

  /**
   * Focus the next input segment from the currently focused segment.
   * Does not wrap when the last segment is focused.
   */
  focusNext: (options?: { selectTextOnFocus?: boolean }) => any;

  /**
   * Clear all segments programmatically.
   * By default, focus is not moved. Set `options.focus` to `true` to focus the first segment after clearing.
   */
  clear: (options?: { focus?: boolean }) => any;
}
