'use strict';
import type { DefaultStyle } from '../../hook/commonTypes';
import type { CSSTimingFunction } from '../easing';
import type { TimeUnit } from './common';
import type { AddArrayPropertyType, AddArrayPropertyTypes } from './helpers';

export interface CSSKeyframesRule {
  readonly cssRules: CSSAnimationKeyframes;
  readonly cssText: string;
  readonly length: number;
  readonly name: string;
}

export type CSSAnimationKeyframeSelector = string | number;
export type CSSAnimationKeyframeBlock<S extends object> = S & {
  animationTimingFunction?: CSSAnimationTimingFunction;
};

export type CSSAnimationKeyframes<S extends object = DefaultStyle> = Record<
  CSSAnimationKeyframeSelector,
  CSSAnimationKeyframeBlock<S>
>;
export type CSSAnimationDuration = TimeUnit;
export type CSSAnimationTimingFunction = CSSTimingFunction;
export type CSSAnimationDelay = TimeUnit;
export type CSSAnimationIterationCount = 'infinite' | number;
export type CSSAnimationDirection =
  | 'normal'
  | 'reverse'
  | 'alternate'
  | 'alternate-reverse';
export type CSSAnimationFillMode = 'none' | 'forwards' | 'backwards' | 'both';
export type CSSAnimationPlayState = 'running' | 'paused';

/** Payload for a CSS animation callback. */
export type CSSAnimationEvent = {
  // TODO: add a JS-side view ref (e.g. `target`) once the right ref type is
  // decided.
  /**
   * The name of the keyframes that fired the event (matches the `name` of a
   * `css.keyframes(...)` rule).
   */
  animationName: string;
  /**
   * The amount of time the animation had been running, in seconds, when the
   * event fired.
   */
  elapsedTime: number;
};

export type CSSAnimationCallback = (event: CSSAnimationEvent) => void;

/**
 * Lifecycle callbacks of a **CSS animation**. `withTiming`, `withSpring` and
 * layout animations never fire them.
 */
export type CSSAnimationCallbacks = {
  /** Fired when the CSS animation starts, after any `animationDelay`. */
  onCSSAnimationStart?: CSSAnimationCallback;
  /** Fired when the CSS animation completes. */
  onCSSAnimationEnd?: CSSAnimationCallback;
  /** Fired at the end of each CSS animation iteration except the last. */
  onCSSAnimationIteration?: CSSAnimationCallback;
  /**
   * Fired when the CSS animation is interrupted before completing, including
   * when the component unmounts.
   */
  onCSSAnimationCancel?: CSSAnimationCallback;
};

export type CSSAnimationCallbackProp = keyof CSSAnimationCallbacks;

export type SingleCSSAnimationSettings = {
  animationDuration?: CSSAnimationDuration;
  animationTimingFunction?: CSSAnimationTimingFunction;
  animationDelay?: CSSAnimationDelay;
  animationIterationCount?: CSSAnimationIterationCount;
  animationDirection?: CSSAnimationDirection;
  animationFillMode?: CSSAnimationFillMode;
  animationPlayState?: CSSAnimationPlayState;
  // animationTimeline?: // TODO - This is still experimental in browsers and we might not want to support it when CSS animations in reanimated are released
};

export type SingleCSSAnimationProperties<S extends object = DefaultStyle> =
  SingleCSSAnimationSettings & {
    animationName: CSSKeyframesRule | CSSAnimationKeyframes<S>;
  };

export type CSSAnimationSettings =
  AddArrayPropertyTypes<SingleCSSAnimationSettings>;

export type CSSAnimationProperties<S extends object = DefaultStyle> =
  CSSAnimationSettings & {
    animationName:
      | AddArrayPropertyType<CSSKeyframesRule | CSSAnimationKeyframes<S>>
      | 'none';
  };

export type ExistingCSSAnimationProperties<S extends object = DefaultStyle> =
  CSSAnimationProperties<S> & {
    animationName: AddArrayPropertyType<
      CSSKeyframesRule | CSSAnimationKeyframes<S>
    >;
  };

export type CSSAnimationProp = keyof CSSAnimationProperties;
