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

export type CarbonSearchMenuContext = {
  query: any;
  shouldFilter: any;
  match: any;
  activeId: any;
  setActiveId: (next: any) => any;
  selectItem: (detail: any) => any;
  hasPrimaryItems: any;
  registerItem: (itemId: any, filterable: any, arg: any) => any;
  unregisterItem: (itemId: any) => any;
};

type $RestProps = SvelteHTMLElements["input"];

type $Props<T = string> = {
  /**
   * Specify the value of the search input.
   * @default ""
   */
  value?: T;

  /**
   * Whether the results menu is visible. Driven by focus and available content.
   * @default false
   */
  open?: boolean;

  /**
   * Set to `true` to filter items by the search value using fuzzy matching
   * @default true
   */
  shouldFilter?: boolean;

  /**
   * Override how the search value is matched against each item's `text`.
   * Receives the item `text` and the current search value, and returns whether
   * the item `matched` along with the `indices` of characters to highlight.
   * Defaults to fuzzy matching. Supply your own function for custom filtering
   * and highlighting, or pass a no-op like `() => ({ matched: true })` to keep
   * every item and disable highlighting.
   * @default fuzzyMatch
   */
  match?: (text: string, query: string) => {
    matched: boolean;
    indices?: number[]
  };

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

  /**
   * Specify the size of the results menu, independent of the input `size`.
   * Defaults to the input `size`.
   * @default undefined
   */
  menuSize?: "xs" | "sm" | "lg" | "xl";

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

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

  /**
   * Set to `true` to render a skeleton menu while results are loading, for
   * example while fetching server-side results. Override the placeholder rows
   * with the `loading` slot.
   * @default false
   */
  loading?: boolean;

  /**
   * Specify the number of skeleton rows rendered while `loading`
   * @default 4
   */
  skeletonCount?: number;

  /**
   * Specify the placeholder text
   * @default "Search..."
   */
  placeholder?: string;

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

  /**
   * Specify the close button label text
   * @default "Clear search input"
   */
  closeButtonLabelText?: string;

  /**
   * Specify the icon to render.
   * @default undefined
   */
  icon?: any;

  /**
   * Specify the direction of the results menu.
   * @default "bottom"
   */
  direction?: "bottom" | "top";

  /**
   * Set to `true` to render the menu in a portal to escape `overflow: hidden` containers
   * @default true
   */
  portal?: boolean;

  /**
   * Specify a class passed to the inner Search element
   * @default ""
   */
  searchClass?: string;

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

  /**
   * Obtain a reference to the input HTML element.
   * @default null
   */
  ref?: null | HTMLInputElement;

  /**
   * Obtain a reference to the menu HTML element.
   * @default null
   */
  menuRef?: null | HTMLDivElement;

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

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

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

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

export type SearchMenuProps<T = string> = Omit<$RestProps, keyof $Props<T>> & $Props<T>;

export default class SearchMenu<T = string> extends SvelteComponentTyped<
  SearchMenuProps<T>,
  {
    blur: WindowEventMap["blur"];
    change: WindowEventMap["change"];
    clear: CustomEvent<any>;
    close: CustomEvent<{ trigger: "escape-key" | "outside-click" | "select" | "blur" }>;
    focus: WindowEventMap["focus"];
    input: WindowEventMap["input"];
    keydown: WindowEventMap["keydown"];
    keyup: WindowEventMap["keyup"];
    paste: WindowEventMap["paste"];
    select: CustomEvent<{
        value: string;
        item: {
          text?: string;
          value?: string;
          href?: string
        };
        event: Event
      }>;
    submit: CustomEvent<{ value: T }>;
  },
  {
    default: Record<string, never>;
    before: Record<string, never>;
    loading: Record<string, never>;
    noResults: Record<string, never>;
  }
> {}
