/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import type { CSSType } from './VarTypes';
import type { CSSProperties } from './StyleXCSSTypes';

export type { CSSProperties } from './StyleXCSSTypes';

// Using an opaque type to declare ClassNames generated by stylex.
declare const StyleXClassNameTag: unique symbol;
export type StyleXClassNameFor<K, V> = string & {
  _opaque: typeof StyleXClassNameTag;
  _key: K;
  _value: V;
};
declare const StyleXVarTag: unique symbol;
declare class _StyleXVar<out Val> {
  private _opaque: typeof StyleXVarTag;
  private _value: Val;
}
export type StyleXVar<Val> = _StyleXVar<Val> & string;

export type StyleXClassNameForValue<V> = StyleXClassNameFor<any, V>;
export type StyleXClassNameForKey<K> = StyleXClassNameFor<K, any>;
export type StyleXClassName = StyleXClassNameFor<any, any>;
// Type for arbitrarily nested Array.
export type StyleXArray<T> = T | ReadonlyArray<StyleXArray<T>>;

type PseudoClassStr = `:${string}`;
type AtRuleStr = `@${string}`;

type CondStr = PseudoClassStr | AtRuleStr;

type CSSPropertiesWithExtras = Partial<
  Readonly<
    CSSProperties & {
      '::after': CSSProperties;
      '::backdrop': CSSProperties;
      '::before': CSSProperties;
      '::cue': CSSProperties;
      '::cue-region': CSSProperties;
      '::first-letter': CSSProperties;
      '::first-line': CSSProperties;
      '::file-selector-button': CSSProperties;
      '::grammar-error': CSSProperties;
      '::marker': CSSProperties;
      // This is a pattern and not a static key so it cannot be typed correctly.
      // [key: `::part(${string})` | `::slotted(${string})`]: CSSProperties;
      '::placeholder': CSSProperties;
      '::selection': CSSProperties;
      // This is a pattern and not a static key so it cannot be typed correctly.
      // '::slotted()': CSSProperties;
      '::spelling-error': CSSProperties;
      '::target-text': CSSProperties;
      '::-webkit-scrollbar'?: CSSProperties;
      '::-webkit-scrollbar-button'?: CSSProperties;
      '::-webkit-scrollbar-thumb'?: CSSProperties;
      '::-webkit-scrollbar-track'?: CSSProperties;
      '::-webkit-scrollbar-track-piece'?: CSSProperties;
      '::-webkit-scrollbar-corner'?: CSSProperties;
      '::-webkit-resizer'?: CSSProperties;
      // webkit styles used for Search in Safari
      '::-webkit-search-decoration'?: CSSProperties;
      '::-webkit-search-cancel-button'?: CSSProperties;
      '::-webkit-search-results-button'?: CSSProperties;
      '::-webkit-search-results-decoration'?: CSSProperties;
      // For input ranges in Chromium
      '::-webkit-slider-thumb'?: CSSProperties;
      '::-webkit-slider-runnable-track'?: CSSProperties;
      // For input ranges in Firefox
      '::-moz-range-thumb'?: CSSProperties;
      '::-moz-range-track'?: CSSProperties;
      '::-moz-range-progress'?: CSSProperties;
    }
  >
>;

export type NestedCSSPropTypes = Partial<
  Readonly<{
    [Key in keyof CSSPropertiesWithExtras]: StyleXClassNameFor<
      Key,
      CSSPropertiesWithExtras[Key]
    >;
  }>
>;

type NotUndefined = {} | null;
type UserAuthoredStyles =
  | CSSPropertiesWithExtras
  | { [key: string]: NotUndefined };
export type StyleXSingleStyle = false | (null | undefined | NestedCSSPropTypes);
// NOTE: `XStyle` has been deprecated in favor of `StaticStyles` and `StyleXStyles`.

export type Keyframes = Readonly<{ [name: string]: CSSProperties }>;

export type PositionTry = Readonly<{
  // Anchor Positioning Properties
  positionAnchor?: CSSProperties['positionAnchor'];
  positionArea?: CSSProperties['positionArea'];
  // inset Properties
  top?: CSSProperties['top'];
  right?: CSSProperties['right'];
  bottom?: CSSProperties['bottom'];
  left?: CSSProperties['left'];
  inset?: CSSProperties['inset'];
  insetBlock?: CSSProperties['insetBlock'];
  insetBlockEnd?: CSSProperties['insetBlockEnd'];
  insetBlockStart?: CSSProperties['insetBlockStart'];
  insetInline?: CSSProperties['insetInline'];
  insetInlineEnd?: CSSProperties['insetInlineEnd'];
  insetInlineStart?: CSSProperties['insetInlineStart'];
  // margin Properties
  margin?: CSSProperties['margin'];
  marginBlock?: CSSProperties['marginBlock'];
  marginBlockEnd?: CSSProperties['marginBlockEnd'];
  marginBlockStart?: CSSProperties['marginBlockStart'];
  marginInline?: CSSProperties['marginInline'];
  marginInlineEnd?: CSSProperties['marginInlineEnd'];
  marginInlineStart?: CSSProperties['marginInlineStart'];
  marginTop?: CSSProperties['marginTop'];
  marginBottom?: CSSProperties['marginBottom'];
  marginLeft?: CSSProperties['marginLeft'];
  marginRight?: CSSProperties['marginRight'];
  // size properties
  width?: CSSProperties['width'];
  height?: CSSProperties['height'];
  minWidth?: CSSProperties['minWidth'];
  minHeight?: CSSProperties['minHeight'];
  maxWidth?: CSSProperties['maxWidth'];
  maxHeight?: CSSProperties['maxHeight'];
  blockSize?: CSSProperties['blockSize'];
  inlineSize?: CSSProperties['inlineSize'];
  minBlockSize?: CSSProperties['minBlockSize'];
  minInlineSize?: CSSProperties['minInlineSize'];
  maxBlockSize?: CSSProperties['maxBlockSize'];
  maxInlineSize?: CSSProperties['maxInlineSize'];
  // self alignment properties
  alignSelf?: CSSProperties['alignSelf'];
  justifySelf?: CSSProperties['justifySelf'];
  placeSelf?: CSSProperties['placeSelf'];
}>;

export type ViewTransitionClass = Readonly<{
  group?: CSSProperties;
  imagePair?: CSSProperties;
  old?: CSSProperties;
  new?: CSSProperties;
}>;

export type LegacyThemeStyles = Readonly<{ [constantName: string]: string }>;

type ComplexStyleValueType<T> =
  T extends StyleXVar<infer U>
    ? U extends CSSType<infer V>
      ? V
      : U
    : T extends string | number | null | symbol
      ? T
      : T extends ReadonlyArray<infer U>
        ? ComplexStyleValueType<U>
        : T extends Readonly<{ default: infer A; [cond: CondStr]: infer B }>
          ? ComplexStyleValueType<A> | ComplexStyleValueType<B>
          : T;

export type MapNamespace<CSS> = Readonly<{
  [Key in keyof CSS]: StyleXClassNameFor<Key, ComplexStyleValueType<CSS[Key]>>;
}>;

export type MapNamespaces<
  S extends {
    [key: string]: UserAuthoredStyles | ((...args: any) => UserAuthoredStyles);
  },
> = Readonly<{
  [Key in keyof S]: S[Key] extends (...args: infer Args) => infer Obj
    ? (...args: Args) => Readonly<[MapNamespace<Obj>, InlineStyles]>
    : MapNamespace<S[Key]>;
}>;

export type StyleX$Create = <
  const S extends {
    [key: string]: UserAuthoredStyles | ((...args: any) => UserAuthoredStyles);
  },
>(
  styles: S,
) => MapNamespaces<S>;

export type CompiledStyles =
  | Readonly<{
      [key: string]: StyleXClassName | null | void | never;
    }>
  | Readonly<{
      theme: StyleXClassName;
    }>;

declare const StyleXInlineStylesTag: unique symbol;

export type InlineStyles = {
  _opaque: typeof StyleXInlineStylesTag;
};

type _GenStylePropType<CSS extends UserAuthoredStyles> = Readonly<{
  [Key in keyof CSS]: StyleXClassNameFor<Key, Readonly<CSS[Key]>>;
}> &
  Partial<{
    [Key in Exclude<keyof CSSPropertiesWithExtras, keyof CSS>]: never;
  }>;

type GenStylePropType<CSS extends UserAuthoredStyles> = Readonly<
  _GenStylePropType<CSS>
>;

// Replace `XStyle` with this.
export type StaticStyles<
  CSS extends UserAuthoredStyles = CSSPropertiesWithExtras,
> = StyleXArray<false | null | undefined | GenStylePropType<CSS>>;

export type StaticStylesWithout<CSS extends UserAuthoredStyles> = StaticStyles<
  Omit<CSSPropertiesWithExtras, keyof CSS>
>;

export type StyleXStyles<
  CSS extends UserAuthoredStyles = CSSPropertiesWithExtras,
> = StyleXArray<
  | null
  | undefined
  | false
  | GenStylePropType<CSS>
  | Readonly<[GenStylePropType<CSS>, InlineStyles]>
>;
export type StyleXStylesWithout<CSS extends UserAuthoredStyles> = StyleXStyles<
  Omit<CSSPropertiesWithExtras, keyof CSS>
>;

declare const StyleXVarGroupTag: unique symbol;
export type VarGroup<
  Tokens extends { [key: string]: any },
  ID extends symbol = symbol,
> = Readonly<{
  [Key in keyof Tokens]: StyleXVar<Tokens[Key]>;
}> &
  Readonly<{
    __opaqueId: ID;
    __tokens: Tokens;
  }> &
  typeof StyleXVarGroupTag;

export type TokensFromVarGroup<T extends VarGroup<{}>> = T['__tokens'];

export type IDFromVarGroup<T extends VarGroup<{}>> = T['__opaqueId'];

type TTokens = Readonly<{
  [key: string]:
    | NestedVarObject<
        null | string | number | StyleXVar<null | string | number>
      >
    | CSSType<null | string | number>;
}>;

type UnwrapVars<T> = T extends () => infer U
  ? UnwrapVars<U>
  : T extends StyleXVar<infer U>
    ? U
    : T;
export type FlattenTokens<T extends TTokens> = Readonly<{
  [Key in keyof T]: T[Key] extends { [key: string]: infer X }
    ? UnwrapVars<X>
    : UnwrapVars<T[Key]>;
}>;

type NestedVarObject<T> =
  | T
  | (() => T)
  | Readonly<{
      default: NestedVarObject<T>;
      [key: AtRuleStr]: NestedVarObject<T>;
    }>;

export type StyleX$DefineConsts = <
  DefaultTokens extends {
    [key: string]: number | string;
  },
>(
  tokens: DefaultTokens,
) => DefaultTokens;

export type StyleX$DefineVars = <
  DefaultTokens extends TTokens,
  ID extends symbol = symbol,
>(
  tokens: DefaultTokens,
) => VarGroup<FlattenTokens<DefaultTokens>, ID>;

declare class ThemeKey<out VG extends VarGroup<{}>> extends String {
  private varGroup: VG;
}
export type Theme<T extends VarGroup<{}>, Tag extends symbol = symbol> = Tag &
  Readonly<{
    theme: StyleXClassNameFor<ThemeKey<T>, IDFromVarGroup<T>>;
  }>;

type OverridesForTokenType<Config extends { [key: string]: unknown }> = {
  [Key in keyof Config]?: NestedVarObject<Config[Key]>;
};

export type StyleX$CreateTheme = <
  TVars extends VarGroup<{}>,
  ThemeID extends symbol = symbol,
>(
  baseTokens: TVars,
  overrides: OverridesForTokenType<TokensFromVarGroup<TVars>>,
) => Theme<TVars, ThemeID>;

export type StyleX$DefineMarker = () => MapNamespace<{
  readonly marker: symbol;
}>;

export type StyleX$When = {
  ancestor: <
    const Pseudo extends `:${string}` | `[${string}]`,
    MarkerSymbol extends symbol = symbol,
  >(
    _pseudo?: Pseudo,
    _customMarker?: MapNamespace<{ readonly marker: MarkerSymbol }>,
    // @ts-expect-error - Trying to use a symbol in a string is not normally allowed
  ) => `:where-ancestor(${Pseudo}, ${MarkerSymbol})`;
  descendant: <
    const Pseudo extends `:${string}` | `[${string}]`,
    MarkerSymbol extends symbol = symbol,
  >(
    _pseudo?: Pseudo,
    _customMarker?: MapNamespace<{ readonly marker: MarkerSymbol }>,
    // @ts-expect-error - Trying to use a symbol in a string is not normally allowed
  ) => `:where-descendant(${Pseudo}, ${MarkerSymbol})`;
  siblingBefore: <
    const Pseudo extends `:${string}` | `[${string}]`,
    MarkerSymbol extends symbol = symbol,
  >(
    _pseudo?: Pseudo,
    _customMarker?: MapNamespace<{ readonly marker: MarkerSymbol }>,
    // @ts-expect-error - Trying to use a symbol in a string is not normally allowed
  ) => `:where-sibling-before(${Pseudo}, ${MarkerSymbol})`;
  siblingAfter: <
    const Pseudo extends `:${string}` | `[${string}]`,
    MarkerSymbol extends symbol = symbol,
  >(
    _pseudo?: Pseudo,
    _customMarker?: MapNamespace<{ readonly marker: MarkerSymbol }>,
    // @ts-expect-error - Trying to use a symbol in a string is not normally allowed
  ) => `:where-sibling-after(${Pseudo}, ${MarkerSymbol})`;
  anySibling: <
    const Pseudo extends `:${string}` | `[${string}]`,
    MarkerSymbol extends symbol = symbol,
  >(
    _pseudo?: Pseudo,
    _customMarker?: MapNamespace<{ readonly marker: MarkerSymbol }>,
    // @ts-expect-error - Trying to use a symbol in a string is not normally allowed
  ) => `:where-any-sibling(${Pseudo}, ${MarkerSymbol})`;
};

export interface Register {}

export type StyleX$Env = Register extends { env: infer TEnv }
  ? TEnv
  : Readonly<{ [key: string]: unknown }>;

// === Nested API Types ===

export type NestedVarsValue =
  | string
  | CSSType<string | number>
  | { readonly [key: string]: NestedVarsValue };

export type NestedConstsValue =
  | string
  | number
  | { readonly [key: string]: NestedConstsValue };

export type NestedStringValue =
  | string
  | { readonly [key: string]: NestedStringValue };

// unstable_defineVarsNested: preserves nested key structure in output.
// Uses generic <T> to give consumers key-level autocomplete.
// Leaf values are replaced with var(--hash) strings at compile time.
export type StyleX$DefineVarsNested = <
  const T extends { [key: string]: NestedVarsValue },
>(
  tokens: T,
) => T;

// unstable_defineConstsNested: same as input — values inlined at compile time.
export type StyleX$DefineConstsNested = <
  const T extends { [key: string]: NestedConstsValue },
>(
  tokens: T,
) => T;

// unstable_createThemeNested: returns a flat theme object like createTheme.
export type StyleX$CreateThemeNested = (
  baseTokens: { readonly [key: string]: NestedStringValue },
  overrides: { readonly [key: string]: NestedVarsValue },
) => CompiledStyles;

// unstable_conditional: identity function for type disambiguation.
// Marks a conditional @media/@supports value so the type system can
// distinguish it from namespace objects in nested token definitions.
export type StyleX$Conditional = <
  const T extends { default: unknown; [key: `@${string}`]: unknown },
>(
  value: T,
) => T;
