import { AriaAttributes } from 'react';

import { Locale } from './constans/locale';

/**
 * Combine all props from given type by Discriminator. It can be useful in internal components,
 * because it allows to get all props in component props destruction.
 *
 * **IMPORTANT**: Do not use this type in public components, because it forces use to fill all component props.
 *
 * ```tsx
 * type Props = PropsOptions<'multi', {values: string[]}, {value: string}>;
 *
 * // The result of type will be
 * type PropsTypeResult =
 *   | {multi: false, value: string, values?: undefined}
 *   | {multi: true, values: string[], value?: undefined};
 *
 * // Then you can use it in you INTERNAL components in props destruction
 * function Component({
 *   multi,
 *   value,
 *   values,
 * }: PropsTypeResult) {
 *   // All variables will be fulled typed
 * }
 * ```
 */
export type FullPropsOptions<Discriminator extends string, TrueDiscriminatorProps, FalseDiscriminatorProps> =
  | (TrueDiscriminatorProps & { [d in Discriminator]: true } & {
      [key in Exclude<keyof FalseDiscriminatorProps, keyof TrueDiscriminatorProps>]?: undefined;
    })
  | (FalseDiscriminatorProps & { [d in Discriminator]: false } & {
      [key in Exclude<keyof TrueDiscriminatorProps, keyof FalseDiscriminatorProps>]?: undefined;
    });

/** General mixin that should be used in each public component to add `testId` prop supporting */
export interface TestIdProps {
  /**
   * Uniq ID that should be used for locate element in test. This ID will be added to
   * the root component HTML element in attribute that configured in {@link HiveUIProps.testIdAttribute}.
   * Also, this ID will be used in all internal child elements.
   *
   * **IMPORTANT**: We strongly don't recommend using `testId` for locate element in your test
   * because it can be a reason of bad tests, but you can do it if you know what do you do!
   */
  testId?: string;
}

export interface CommonProps extends TestIdProps {
  /**
   * The additional class is applied to the root element of the component.
   * It allows to customize component via styled-component.
   */
  className?: string;
  /**
   * Identifies the element (or elements) that describes the current element
   *
   * @see https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-describedby
   */
  ariaDescribedBy?: AriaAttributes['aria-describedby'];
}

/**
 * A string with a [BCP 47]{@link https://datatracker.ietf.org/doc/html/rfc5646} language tag.
 *
 * {@link Locale} enumeration is the subset of the most popular values.
 * It is used here to improve code auto-completion, despite the fact that as the identifier can be used any other language tag
 *
 * @example
 * "en" // represents English
 * @example
 * "en-US" // represents American English
 * @example
 * "gsw-u-sd-chzh" // represents Swiss German as used in the Canton of Zürich.
 * @example
 * "ar-u-nu-latn"
 * // represents Arabic-language content using Basic Latin digits (0 through 9)
 * // instead of Arabic-script digits (٠ through ٩).
 */
export type LocaleIdentifier = Locale | string;

/**
 * Utils to get deep keyof
 * https://stackoverflow.com/questions/58434389/typescript-deep-keyof-of-a-nested-object
 */
type Join<K, P> = K extends string | number
  ? P extends string | number
    ? `${K}${'' extends P ? '' : '.'}${P}`
    : never
  : never;

type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...0[]];

export type Paths<T, D extends number = 10> = [D] extends [never]
  ? never
  : T extends object
  ? {
      [K in keyof T]-?: K extends string | number ? `${K}` | Join<K, Paths<T[K], Prev[D]>> : never;
    }[keyof T]
  : '';

export type Leaves<T, D extends number = 10> = [D] extends [never]
  ? never
  : T extends object
  ? { [K in keyof T]-?: Join<K, Leaves<T[K], Prev[D]>> }[keyof T]
  : '';
