import { BaseComponent } from '../../types';
import { create, find, isHTMLElement, remove } from '../../utils';

/**
 * The interface for the Style component.
 *
 * @since 3.0.0
 */
export interface StyleComponent extends BaseComponent {
  rule( selector: string, prop: string, value: string | number ): void;
  ruleBy( target: string | HTMLElement, prop: string, value: string | number ): void;
}

/**
 * The component for managing styles of the slider.
 *
 * @since 3.0.0
 *
 * @return A Style component object.
 */
export function Style(): StyleComponent {
  /**
   * The style element for the slider.
   */
  let style: HTMLStyleElement;

  /**
   * The CSSStyleSheet object of the created style element.
   */
  let sheet: CSSStyleSheet;

  /**
   * Called when the component is mounted.
   */
  function mount(): void {
    style = create( 'style', {}, document.head );
    sheet = style.sheet;
  }

  /**
   * Destroys the component.
   */
  function destroy(): void {
    remove( style );
    sheet = null;
  }

  /**
   * Registers the style for the selector.
   *
   * @param selector - A selector string.
   * @param prop     - A CSS property, accepting the camel case.
   * @param value    - A CSS value.
   */
  function rule( selector: string, prop: string, value: string | number ): void {
    const { cssRules } = sheet;
    const cssRule = find( cssRules, cssRule => isCSSStyleRule( cssRule ) && cssRule.selectorText === selector )
      || cssRules[ sheet.insertRule( `${ selector }{}`, 0 ) ];

    if ( isCSSStyleRule( cssRule ) ) {
      cssRule.style[ prop ] = `${ value }`;
    }
  }

  /**
   * Registers the style by the element or the ID.
   *
   * @param target - A target element or ID.
   * @param prop   - A CSS property, accepting the camel case.
   * @param value  - A CSS value.
   */
  function ruleBy( target: string | HTMLElement, prop: string, value: string | number ): void {
    rule( `#${ isHTMLElement( target ) ? target.id : target }`, prop, value );
  }

  /**
   * Checks if the provided rule is a CSSStyleRule instance or not.
   *
   * @param cssRule - An instance to check.
   *
   * @return `true` if the cssRule is an instance of CSSStyleRule, or otherwise `false`.
   */
  function isCSSStyleRule( cssRule: CSSRule ): cssRule is CSSStyleRule {
    return cssRule instanceof CSSStyleRule;
  }

  return {
    mount,
    destroy,
    rule,
    ruleBy,
  };
}
