import { camelize, dasherize, cssText, toCSS, forEach, keys, reduce, map, trim } from 'dinosheets/utils';

var sheet;
function testerSheet() {
  if (sheet) {
    return sheet;
  }

  const element = document.createElement('style');
  const head = document.getElementsByTagName('head')[0] ||
               document.documentElement;
  element.type = 'text/css';
  head.appendChild(element);

  return sheet = document.styleSheets[document.styleSheets.length - 1];
}
const supportedCssRules = {};

class StyleSheetShim {
  constructor(styleSheet) {
    this.styleSheet = styleSheet;
    this.indexes = {};
    this.styles = {};
  }

  rules() {
    const { styleSheet } = this;
    return styleSheet.cssRules || styleSheet.rules || [];
  }

  ruleFor(selector) {
    const index = this.indexes[selector];
    if (index == null) { return null; }
    const rules = this.rules();

    return {
      index,
      rule: rules[index]
    };
  }

  canApplyRule(rule, value) {
    if (supportedCssRules.hasOwnProperty(rule)) {
      return supportedCssRules[rule];
    }

    const styleSheet = testerSheet();
    const selector = '#dinosheet---tester---selector';
    const css = `${dasherize(rule)}: ${value};`;

    if (styleSheet.insertRule) {
      styleSheet.insertRule(`${selector} { ${css} }`, 0);
    } else {
      styleSheet.addRule(selector, css, 0);
    }

    const rules = sheet.cssRules || sheet.rules || [];
    if (cssText(rules[0]).length > selector.length + css.length) {
      supportedCssRules[rule] = true;
    } else {
      supportedCssRules[rule] = false;
    }

    if (styleSheet.deleteRule) {
      styleSheet.deleteRule(selector, 0);
    } else {
      styleSheet.removeRule(0);
    }
    return supportedCssRules[rule];
  }

  insertRule(selector, rules, index) {
    const { styleSheet } = this;

    // Lint out all rules that can't be applied
/*    rules = reduce(keys(rules), (E, rule) => {
      let value = rules[rule];
      if (this.canApplyRule(rule, value)) {
        E[rule] = value;
      }
      return E;
    }, {});*/

    const css = toCSS(rules);

    if (index == null) {
      index = this.rules().length;
    }

    if (css.length) {
      this.indexes[selector] = index;
      this.styles[selector] = rules;
      if (styleSheet.insertRule) {
        styleSheet.insertRule(`${selector} { ${css} }`, index);
      } else {
        styleSheet.addRule(selector, css, index);

        if (this.ruleFor(selector).rule.selectorText.toLowerCase() !== selector.toLowerCase()) {
          delete this.indexes[selector];
          delete this.styles[selector];
          this._splitsSelectors = true;
          forEach(map(selector.split(','), trim), (selector) => {
            this.indexes[selector] = index++;
            this.styles[selector] = rules;
          });
        }
      }
    }
  }

  updateRule(selector, rules) {
    if (this._splitsSelectors && selector.indexOf(',') !== -1) {
      forEach(map(selector.split(','), trim), (selector) => {
        this.updateRule(selector, rules);
      });
      return;
    }

    // Lint out all rules that can't be applied
/*    rules = reduce(keys(rules), (E, rule) => {
      let value = rules[rule];
      if (this.canApplyRule(rule, value)) {
        E[rule] = value;
      }
      return E;
    }, {});*/

    const { rule } = this.ruleFor(selector);
    for (let key in rules) {
      if (!rules.hasOwnProperty(key)) { continue; }
      let value = rules[key];
      key = camelize(key);
      if (key == null) { continue; }

      if (value == null) {
        delete this.styles[selector][key];
        if (rule.style.removeProperty) {
          rule.style.removeProperty(dasherize(key));
        } else {
          rule.style[key] = null;
        }
      } else {
        this.styles[selector][key] = value;
        rule.style[key] = value;
      }
    }
  }

  insertOrUpdateRule(selector, rules) {
    if (this.ruleFor(selector)) {
      this.updateRule(selector, rules);
    } else {
      this.insertRule(selector, rules);
    }
  }

  deleteRule(selector) {
    if (this._splitsSelectors && selector.indexOf(',') !== -1) {
      forEach(map(selector.split(','), trim), (selector) => {
        this.deleteRule(selector);
      });
      return;
    }

    const { styleSheet } = this;
    const { index } = this.ruleFor(selector);
    if (styleSheet.deleteRule) {
      styleSheet.deleteRule(selector, index);
    } else {
      styleSheet.removeRule(index);
    }
    delete this.indexes[selector];
    delete this.styles[selector];
  }

  toString() {
    let sortedStyles = [];
    let styles = this.styles;
    let indexes = this.indexes;

    for (let selector in styles) {
      let rules = styles[selector];
      let index = indexes[selector];
      sortedStyles[index] = [selector, rules];
    }

    let css = [];
    for (let i = 0, len = sortedStyles.length; i < len; i++) {
      let style = sortedStyles[i];
      if (style == null) { continue; }
      let selector = style[0];
      let rules = style[1];
      css.push([
        `${selector} {\n`,
        toCSS(rules, { tab: '  ', newline: '\n' }),
        '}'
      ].join(''));
    }
    return css.join('\n\n');
  }
}

export default StyleSheetShim;

@@ -1854,34 +1854,32 @@
 an't be applied%0A
-/*
     rules = redu
@@ -2036,34 +2036,32 @@
 n E;%0A    %7D, %7B%7D);
-*/
 %0A%0A    const css 
@@ -3106,10 +3106,8 @@
 ied%0A
-/*
     
@@ -3288,10 +3288,8 @@
 %7B%7D);
-*/
 %0A%0A  

Revert "temporarily remove canApplyRule calls to see where failures are in IE8"