// import { atomic } from 'react-native-web/dist/exports/StyleSheet/compiler';
import { atomic } from '../react-native-web-fucntions/atomic';
import preprocess from '../react-native-web-fucntions/preprocess';
import type {
  DataSet,
  Query,
  UseResponsiveQueryParams,
  UseResponsiveQueryReturnType,
  GetResponsiveStylesParams,
} from './types';
import { StyleSheet } from 'react-native';
// @ts-ignore
import stableHash from 'stable-hash';
import hash from './hash';
import type { GetResponsiveStylesReturnType } from './types';
import { useStableMemo } from './useStableMemo';
import { getResponsiveStylesImpl, useDimensionsWithEnable } from './common';
import { ResponsiveQueryContext } from './ResponsiveQueryProvider';
import React from 'react';

/******************** Implementation after RNW v0.18 ***********************/

/**
 * 1. preprocess:- Handles shadow/text shadow conversion from RN styles to web *                 styles
 *
 * 2. atomic:- it handles prefixing, converting RN specific styles to web styles *             and generating the CSS selector.
 *             Input {marginTop: 10}
 *             Output
 *             compiledStyle : marginTop: "r-marginTop-156q2ks"
 *             compiledOrderedRules : ".r-marginTop-156q2ks{margin-top:10px;}"
 *      a)compiledStyle:- Array it holds identifier/selector with properties
 *      b)compiledOrderedRules:- Array it holds the css rule with selector name
 *      Also from RNW v0.18 handles swapping of ltr styles if enabled by user
 * 3. createSheet:- used to grab sheet which exist already created by rnw. when we *                  call createSheet without id it will return sheet which exist. *                  cause it automatically takes a default ID  which is already in *                  use (created by rnw with default ID) so this return sheet *                  which exist with  ID doesn't create a new sheet.
 *
 * This Implementation is based on asumptions that RNW doesn't change the         * function  or doesn't re-write them. if there is any change in RNW implmentation * it we'll break and needs to be updated.
 *
 */
let textContentMap: any = {};

export const useResponsiveQuery = (
  queries?: UseResponsiveQueryParams
): UseResponsiveQueryReturnType => {
  const responsiveQueryContext = React.useContext(ResponsiveQueryContext);
  const disableCSSMediaQueries =
    queries?.disableCSSMediaQueries ??
    responsiveQueryContext.disableCSSMediaQueries;

  // Only attaches listener if disableCSSMediaQueries is true
  const windowWidth = useDimensionsWithEnable({
    enable: disableCSSMediaQueries,
  }).width;

  const values = useStableMemo(() => {
    // Use the non-media query responsive styling
    if (disableCSSMediaQueries) {
      const getResponsiveStyles = getResponsiveStylesImpl(windowWidth);
      if (queries) {
        const { styles } = getResponsiveStyles(queries);
        return { styles, getResponsiveStyles };
      } else {
        return { getResponsiveStyles };
      }
    } else {
      if (queries) {
        const { styles, dataSet } = getResponsiveStyles(queries);
        return { dataSet, styles, getResponsiveStyles };
      } else {
        return { getResponsiveStyles };
      }
    }
  }, [queries, windowWidth, disableCSSMediaQueries]);

  return values;
};

const getDataAttribute = (queryRule: Query) => {
  if (
    typeof queryRule.minWidth === 'number' &&
    typeof queryRule.maxWidth === 'number'
  ) {
    return `min-width-${queryRule.minWidth}-max-width-${queryRule.maxWidth}`;
  } else if (typeof queryRule.minWidth === 'number') {
    return `min-width-${queryRule.minWidth}`;
  } else if (typeof queryRule.maxWidth === 'number') {
    return `max-width-${queryRule.maxWidth}`;
  }

  return undefined;
};

const getMediaQueryRule = (query: Query, newRule: string) => {
  if (
    typeof query.minWidth === 'number' &&
    typeof query.maxWidth === 'number'
  ) {
    return `@media only screen and (min-width: ${query.minWidth}px) and (max-width: ${query.maxWidth}px) { ${newRule} }`;
  } else if (typeof query.minWidth === 'number') {
    return `@media only screen and (min-width: ${query.minWidth}px) { ${newRule} }`;
  } else if (typeof query.maxWidth === 'number') {
    return `@media only screen and (max-width: ${query.maxWidth}px) { ${newRule} }`;
  }
  return undefined;
};
let styleSheet: any;
const insert = (rule: string) => {
  if (rule === '') {
    return;
  }
  if (typeof window !== 'undefined') {
    if (!styleSheet) {
      const styleEl = document.createElement('style');
      styleEl.type = 'text/css';
      styleEl.appendChild(document.createTextNode(''));
      document.head.appendChild(styleEl);
      styleSheet = styleEl.sheet;
    }

    styleSheet.insertRule(rule, styleSheet.cssRules.length);
  }
};
const getResponsiveStyles = (
  queries: GetResponsiveStylesParams
): GetResponsiveStylesReturnType => {
  const queryString = stableHash(queries.query);

  const queriesHash = hash(queryString);

  const styles = queries.initial
    ? [
        StyleSheet.create({ initial: StyleSheet.flatten(queries.initial) })
          .initial,
      ]
    : undefined;

  let dataSet: DataSet = {};

  /**
   * This function is copied from intergalacticspacehighway/rnw-responsive-ssr
   */

  if (queries.query) {
    queries.query.forEach((queryRule) => {
      const queryHash = queriesHash + hash(stableHash(queryRule));
      const dataAttribute = getDataAttribute(queryRule);

      if (dataAttribute) {
        const newIdentifier = `[data-${dataAttribute}$="${queryHash}"]`;
        dataSet[dataAttribute] = queryHash;

        let mediaRules = '';

        const flattenQueryStyle = StyleSheet.flatten(queryRule.style);
        const newStyle = preprocess(flattenQueryStyle);
        const [compiledStyle, compiledOrderedRules] = atomic(newStyle);
        //@ts-ignore
        delete compiledStyle.$$css; //removing unnecessary $$css property
        Object.keys(compiledStyle).forEach((key) => {
          const oldIdentifier = compiledStyle[key];
          compiledOrderedRules.forEach(([rules, _order]: any) => {
            // Rule returned by atomic has css selectors, so we'll replace it with data-attr selector

            let newRule = '';
            if (rules[0].includes(oldIdentifier)) {
              newRule = rules[0].replace('.' + oldIdentifier, newIdentifier);
            }
            mediaRules += newRule;
          });
        });
        if (mediaRules) {
          const mediaQueryRule = getMediaQueryRule(queryRule, mediaRules);

          const queryKey = `/*${queryHash}{}*/${mediaQueryRule}`;

          if (!textContentMap[queryKey]) {
            insert(queryKey);
            textContentMap[queryKey] = true;
          }
        }
      }
    });
  }

  return { styles, dataSet };
};

/**
 * This function is copied from intergalacticspacehighway/rnw-responsive-ssr
 */
export const getStyleElement = () => {
  return (
    <style
      type="text/css"
      dangerouslySetInnerHTML={{ __html: Object.keys(textContentMap).join('') }}
    />
  );
};
