import { Assert, UnitTest } from '@ephox/bedrock-client';
import { Arr, Optional } from '@ephox/katamari';
import { KAssert } from '@ephox/katamari-assertions';
import { PlatformDetection } from '@ephox/sand';

import * as Insert from 'ephox/sugar/api/dom/Insert';
import * as Remove from 'ephox/sugar/api/dom/Remove';
import * as SugarBody from 'ephox/sugar/api/node/SugarBody';
import * as Attribute from 'ephox/sugar/api/properties/Attribute';
import * as Css from 'ephox/sugar/api/properties/Css';
import Div from 'ephox/sugar/test/Div';
import MathElement from 'ephox/sugar/test/MathElement';

UnitTest.test('CssTest', () => {
  const runChecks = (connected: boolean) => {
    const c = Div();
    const m = MathElement();
    if (connected) {
      Insert.append(SugarBody.body(), c);
    }

    Insert.append(SugarBody.body(), m);

    const check = (k: string, v1: string, v2: string) => {
      Css.set(c, k, v1);
      Css.set(m, k, v1); // Just checking that the element
      Assert.eq('get', v1, Css.get(c, k));
      Css.set(c, k, v2);
      Assert.eq('get', v2, Css.get(c, k));
    };

    check('background-color', 'rgb(10, 20, 30)', 'rgb(40, 50, 11)');
    check('display', 'none', 'block');

    Css.set(c, 'position', 'relative'); // so that z-index actually does something
    check('z-index', '-1', '2');

    const c2 = Div();
    Css.copy(c, c2);
    Css.copy(m, c2);

    // NOTE: Safari, Firefox 71+ and Chromium 102+ seem to support styles for math ml tags, so the Css.copy(m, c2) clobbers the previous style
    const browser = PlatformDetection.detect().browser;
    if (browser.isSafari() || browser.isFirefox() && browser.version.major >= 71 || browser.isChromium() && browser.version.major >= 102) {
      Css.copy(c, c2);
    }

    Css.get(m, 'display');
    Css.getRaw(m, 'bogus');

    Assert.eq('get', 'rgb(40, 50, 11)', Css.get(c2, 'background-color'));
    Assert.eq('get', 'block', Css.get(c2, 'display'));

    // getRaw
    const d = Div();
    if (connected) {
      Insert.append(SugarBody.body(), d);
    }
    KAssert.eqNone('getRaw bogus', Css.getRaw(d, 'bogus'));

    Assert.eq('getRaw display 1', true, Css.getRaw(d, 'display').isNone());
    Css.set(d, 'display', 'inline-block');
    Assert.eq('getRaw display 2', true, Css.getRaw(d, 'display').isSome());
    Assert.eq('getRaw display 3', 'inline-block', Css.getRaw(d, 'display').getOrDie('Optional expecting: inline-block'));
    Css.remove(d, 'display');
    Assert.eq('getRaw display 4', true, Css.getRaw(d, 'display').isNone());
    Assert.eq('has', false, Attribute.has(d, 'style'));
    Css.set(d, 'font-size', '12px');
    Assert.eq('getRaw font-size 1', true, Css.getRaw(d, 'font-size').isSome());
    Css.remove(d, 'font-size');
    Assert.eq('getRaw font-size 2', false, Css.getRaw(d, 'font-size').isSome());
    Css.set(d, 'background-color', 'rgb(12, 213, 12)');
    Assert.eq('getRaw background-color', 'rgb(12, 213, 12)', Css.getRaw(d, 'background-color').getOrDie('Optional expecting: rgb(12,213,12)'));
    Css.remove(d, 'background-color');

    // getAllRaw
    const bulkStyles = {
      'display': 'inline-block',
      'font-size': '12px',
      'background-color': 'rgb(12, 213, 12)'
    };

    Css.setAll(d, bulkStyles);
    Assert.eq('getAllRaw', bulkStyles, Css.getAllRaw(d));
    Attribute.remove(d, 'style');

    // validate
    Assert.eq('isValidValue', true, Css.isValidValue('span', 'font-size', 'small'));
    Assert.eq('isValidValue', true, Css.isValidValue('span', 'font-size', '12px'));
    Assert.eq('isValidValue', false, Css.isValidValue('span', 'font-size', 'biggest'));
    Assert.eq('isValidValue', true, Css.isValidValue('span', 'display', 'inline-block'));
    Assert.eq('isValidValue', false, Css.isValidValue('span', 'display', 'on'));
    Assert.eq('isValidValue', true, Css.isValidValue('span', 'background-color', '#232323'));
    Assert.eq('isValidValue', false, Css.isValidValue('span', 'backgroundColor', '#2323'));
    Assert.eq('isValidValue', false, Css.isValidValue('span', 'font-size', 'value'));
    Assert.eq('isValidValue', true, Css.isValidValue('span', 'margin-top', '23px'));

    const play = Div();
    if (connected) {
      Insert.append(SugarBody.body(), play);
    }

    // ensure preserve works correctly when there are no styles
    Css.preserve(play, (e) => {
      Css.set(e, 'left', '0px');
    });
    if (!(Attribute.get(play, 'style') === '' || Attribute.get(play, 'style') === undefined)) {
      Assert.fail('lack of styles should have been preserved, was "' + Attribute.get(play, 'style') + '"');
    }

    Css.setAll(play, {
      'left': '0px',
      'right': '0px',
      'font-size': '12px'
    });
    Assert.eq('getRaw', true, Css.getRaw(play, 'font-size').isSome());
    Css.preserve(play, (el) => {
      Css.remove(el, 'font-size');
      Assert.eq('getRaw', false, Css.getRaw(play, 'font-size').isSome());
    });
    Assert.eq('Font size should have been preserved', true, Css.getRaw(play, 'font-size').isSome());

    Css.setOptions(play, {
      'left': Optional.none(),
      'right': Optional.none(),
      'top': Optional.some('0px'),
      'bottom': Optional.some('0px'),
      'font-size': Optional.none(),
      'font-family': Optional.some('Arial')
    });

    KAssert.eqNone('getRaw left', Css.getRaw(play, 'left'));
    KAssert.eqNone('getRaw right', Css.getRaw(play, 'right'));
    KAssert.eqNone('getRaw font-size', Css.getRaw(play, 'font-size'));
    KAssert.eqSome('getRaw top', '0px', Css.getRaw(play, 'top'));
    KAssert.eqSome('getRaw bottom', '0px', Css.getRaw(play, 'bottom'));
    KAssert.eqSome('getRaw font-family', 'Arial', Css.getRaw(play, 'font-family'));

    // final cleanup
    Arr.each([ c, m, d, play ], Remove.remove);

  };

  runChecks(true);
  runChecks(false);
});
