import { describe, test, expect } from '@jest/globals';
import { Styles, StyleList } from '#js/util/Styles.js';

function cssTest(css: string, list: StyleList, text: string = css + ';') {
  const styles = new Styles(css);
  expect(styles.styleList).toEqual(list);
  expect(styles.cssText).toBe(text);
}

function cssFontTest(css: string, list: StyleList) {
  const test = Array.from(Object.keys(list))
    .map((k) => `${k}: ${list[k]};`)
    .join(' ');
  return cssTest(css, list, test);
}

describe('CssStyles object', () => {
  test('Styles parsing', () => {
    cssTest('', {}, ''); // emtpy list
    cssTest('font-size: 150%', { 'font-size': '150%' }); // one style
    cssTest('abc-def: 0', { 'abc-def': '0' }); // unknown style is retained
    cssTest('a: 0; b: 1px', { a: '0', b: '1px' }); // multiple styles
    cssTest(
      'a: 0; /*b: 1px*/ c: bold',
      { a: '0', c: 'bold' },
      'a: 0; c: bold;'
    ); // comments
    cssTest(
      '  a  : \n  0  ;   b\n :   1px \n ',
      { a: '0', b: '1px' },
      'a: 0; b: 1px;'
    ); // extra spaces
    cssTest('abc: ; xyz: 0', { xyz: '0' }, 'xyz: 0;'); // missing value
    cssTest('abc xyz: 1px', {}, ''); // malformed CSS string

    cssTest(`abc: xy 'pqr`, { abc: `xy 'pqr'` }, `abc: xy 'pqr';`); // append missing '
    cssTest(`abc: xy "pqr`, { abc: `xy "pqr"` }, `abc: xy "pqr";`); // append missing "
    cssTest(`abc: xy '\\'pqr`, { abc: `xy '\\'pqr'` }, `abc: xy '\\'pqr';`); // handle quoted '
    cssTest(`abc: xy "\\"pqr`, { abc: `xy "\\"pqr"` }, `abc: xy "\\"pqr";`); // handle quoted "
    cssTest(`abc: ';'`, { abc: `';'` }); // handle quoted ;
    cssTest(`abc: ';`, { abc: `';'` }, `abc: ';';`); // and missing '

    cssTest(
      '\nabc\n:\n xyz \n; \n def \n : \n pqr\n ; \n ',
      {
        abc: 'xyz',
        def: 'pqr',
      },
      'abc: xyz; def: pqr;'
    );
    cssTest(
      `abc: \n xy \n 'pqr\n`,
      { abc: `xy   'pqr '` },
      `abc: xy   'pqr ';`
    );

    //
    // Remove unquoted ; and beyond
    //
    const styles = new Styles();
    styles.set('abc', 'xy ; z');
    expect(styles.styleList).toEqual({ abc: 'xy' });
    expect(styles.cssText).toBe('abc: xy;');
  });

  test('padding', () => {
    cssTest('padding: 3px', {
      padding: '3px',
      'padding-bottom': '3px',
      'padding-left': '3px',
      'padding-right': '3px',
      'padding-top': '3px',
    });
    cssTest(
      'padding: 3px; padding-right: 1px',
      {
        padding: '3px 1px 3px 3px',
        'padding-bottom': '3px',
        'padding-left': '3px',
        'padding-right': '1px',
        'padding-top': '3px',
      },
      'padding: 3px 1px 3px 3px;'
    );
    cssTest(
      'padding-top: 0; padding-right: 1px; padding-bottom: 0; padding-left: 1px',
      {
        padding: '0 1px',
        'padding-bottom': '0',
        'padding-left': '1px',
        'padding-right': '1px',
        'padding-top': '0',
      },
      'padding: 0 1px;'
    );
    cssTest(
      'padding-top: 0; padding-right: 1px; padding-bottom: 2px; padding-left: 1px',
      {
        padding: '0 1px 2px',
        'padding-bottom': '2px',
        'padding-left': '1px',
        'padding-right': '1px',
        'padding-top': '0',
      },
      'padding: 0 1px 2px;'
    );
    cssTest(
      'padding-top: 0; padding-right: 0; padding-bottom: 0; padding-left: 0',
      {
        padding: '0',
        'padding-bottom': '0',
        'padding-left': '0',
        'padding-right': '0',
        'padding-top': '0',
      },
      'padding: 0;'
    );
    cssTest(
      'padding-left: 2px; padding: 0',
      {
        padding: '0',
        'padding-bottom': '0',
        'padding-left': '0',
        'padding-right': '0',
        'padding-top': '0',
      },
      'padding: 0;'
    );
    cssTest('padding:', {}, '');
  });

  test('margin', () => {
    cssTest(
      'margin-left: 2px; margin: 0',
      {
        margin: '0',
        'margin-bottom': '0',
        'margin-left': '0',
        'margin-right': '0',
        'margin-top': '0',
      },
      'margin: 0;'
    );
    cssTest('margin: 3px', {
      margin: '3px',
      'margin-bottom': '3px',
      'margin-left': '3px',
      'margin-right': '3px',
      'margin-top': '3px',
    });
    cssTest(
      'margin: 3px; margin-right: 1px',
      {
        margin: '3px 1px 3px 3px',
        'margin-bottom': '3px',
        'margin-left': '3px',
        'margin-right': '1px',
        'margin-top': '3px',
      },
      'margin: 3px 1px 3px 3px;'
    );
    cssTest(
      'margin-top: 0; margin-right: 1px; margin-bottom: 0; margin-left: 1px',
      {
        margin: '0 1px',
        'margin-bottom': '0',
        'margin-left': '1px',
        'margin-right': '1px',
        'margin-top': '0',
      },
      'margin: 0 1px;'
    );
    cssTest(
      'margin-top: 0; margin-right: 1px; margin-bottom: 2px; margin-left: 1px',
      {
        margin: '0 1px 2px',
        'margin-bottom': '2px',
        'margin-left': '1px',
        'margin-right': '1px',
        'margin-top': '0',
      },
      'margin: 0 1px 2px;'
    );
    cssTest(
      'margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0',
      {
        margin: '0',
        'margin-bottom': '0',
        'margin-left': '0',
        'margin-right': '0',
        'margin-top': '0',
      },
      'margin: 0;'
    );
    cssTest(
      'margin-left: 2px; margin: 0',
      {
        margin: '0',
        'margin-bottom': '0',
        'margin-left': '0',
        'margin-right': '0',
        'margin-top': '0',
      },
      'margin: 0;'
    );
    cssTest('margin:', {}, '');
  })

  test('border', () => {
    cssTest('border: 3px solid red', {
      border: '3px solid red',
      'border-top': '3px solid red',
      'border-top-color': 'red',
      'border-top-style': 'solid',
      'border-top-width': '3px',
      'border-right': '3px solid red',
      'border-right-color': 'red',
      'border-right-style': 'solid',
      'border-right-width': '3px',
      'border-bottom': '3px solid red',
      'border-bottom-color': 'red',
      'border-bottom-style': 'solid',
      'border-bottom-width': '3px',
      'border-left': '3px solid red',
      'border-left-color': 'red',
      'border-left-style': 'solid',
      'border-left-width': '3px',
    });
    cssTest(
      'border: 3px solid red; border-top: inset blue 2px',
      {
        'border-top': 'inset blue 2px',
        'border-top-color': 'blue',
        'border-top-style': 'inset',
        'border-top-width': '2px',
        'border-right': '3px solid red',
        'border-right-color': 'red',
        'border-right-style': 'solid',
        'border-right-width': '3px',
        'border-bottom': '3px solid red',
        'border-bottom-color': 'red',
        'border-bottom-style': 'solid',
        'border-bottom-width': '3px',
        'border-left': '3px solid red',
        'border-left-color': 'red',
        'border-left-style': 'solid',
        'border-left-width': '3px',
      },
      [
        'border-top: inset blue 2px; border-right: 3px solid red;',
        'border-bottom: 3px solid red; border-left: 3px solid red;',
      ].join(' ')
    );
    cssTest(
      'border: 3px solid red; border-top-color: blue',
      {
        'border-top': '3px solid blue',
        'border-top-color': 'blue',
        'border-top-style': 'solid',
        'border-top-width': '3px',
        'border-right': '3px solid red',
        'border-right-color': 'red',
        'border-right-style': 'solid',
        'border-right-width': '3px',
        'border-bottom': '3px solid red',
        'border-bottom-color': 'red',
        'border-bottom-style': 'solid',
        'border-bottom-width': '3px',
        'border-left': '3px solid red',
        'border-left-color': 'red',
        'border-left-style': 'solid',
        'border-left-width': '3px',
      },
      [
        'border-top: 3px solid blue; border-right: 3px solid red;',
        'border-bottom: 3px solid red; border-left: 3px solid red;',
      ].join(' ')
    );
    cssTest(
      'border-top: 3px solid red; border-top-color: blue',
      {
        'border-top': '3px solid blue',
        'border-top-color': 'blue',
        'border-top-style': 'solid',
        'border-top-width': '3px',
      },
      'border-top: 3px solid blue;'
    );
    cssTest(
      'border-top: 3px solid red; border-top-style: groove',
      {
        'border-top': '3px groove red',
        'border-top-color': 'red',
        'border-top-style': 'groove',
        'border-top-width': '3px',
      },
      'border-top: 3px groove red;'
    );
    cssTest(
      'border-top: 3px solid red; border-top-width: 2px',
      {
        'border-top': '2px solid red',
        'border-top-color': 'red',
        'border-top-style': 'solid',
        'border-top-width': '2px',
      },
      'border-top: 2px solid red;'
    );
    cssTest('border: 3px solid', {
      border: '3px solid',
      'border-bottom': '3px solid',
      'border-bottom-style': 'solid',
      'border-bottom-width': '3px',
      'border-left': '3px solid',
      'border-left-style': 'solid',
      'border-left-width': '3px',
      'border-right': '3px solid',
      'border-right-style': 'solid',
      'border-right-width': '3px',
      'border-top': '3px solid',
      'border-top-style': 'solid',
      'border-top-width': '3px',
    });
    cssTest('border: 3px blue', {
      border: '3px blue',
      'border-bottom': '3px blue',
      'border-bottom-color': 'blue',
      'border-bottom-width': '3px',
      'border-left': '3px blue',
      'border-left-color': 'blue',
      'border-left-width': '3px',
      'border-right': '3px blue',
      'border-right-color': 'blue',
      'border-right-width': '3px',
      'border-top': '3px blue',
      'border-top-color': 'blue',
      'border-top-width': '3px',
    });
    cssTest('border: solid blue', {
      border: 'solid blue',
      'border-bottom': 'solid blue',
      'border-bottom-color': 'blue',
      'border-bottom-style': 'solid',
      'border-left': 'solid blue',
      'border-left-color': 'blue',
      'border-left-style': 'solid',
      'border-right': 'solid blue',
      'border-right-color': 'blue',
      'border-right-style': 'solid',
      'border-top': 'solid blue',
      'border-top-color': 'blue',
      'border-top-style': 'solid',
    });
    cssTest(
      'border-top: red; border-right: red; border-bottom: red; border-left: red',
      {
        border: 'red',
        'border-bottom': 'red',
        'border-bottom-color': 'red',
        'border-left': 'red',
        'border-left-color': 'red',
        'border-right': 'red',
        'border-right-color': 'red',
        'border-top': 'red',
        'border-top-color': 'red',
      },
      'border: red;'
    );
    cssTest(
      'border: 3px solid red; border-width: 2px',
      {
        border: '2px solid red',
        'border-bottom': '2px solid red',
        'border-bottom-color': 'red',
        'border-bottom-style': 'solid',
        'border-bottom-width': '2px',
        'border-left': '2px solid red',
        'border-left-color': 'red',
        'border-left-style': 'solid',
        'border-left-width': '2px',
        'border-right': '2px solid red',
        'border-right-color': 'red',
        'border-right-style': 'solid',
        'border-right-width': '2px',
        'border-top': '2px solid red',
        'border-top-color': 'red',
        'border-top-style': 'solid',
        'border-top-width': '2px',
      },
      'border: 2px solid red;'
    );
    cssTest(
      'border: red; border-left-color:',
      {
        'border-bottom': 'red',
        'border-bottom-color': 'red',
        'border-right': 'red',
        'border-right-color': 'red',
        'border-top': 'red',
        'border-top-color': 'red',
      },
      'border-top: red; border-right: red; border-bottom: red;'
    );
    cssTest('border-radius: 3px', {
      'border-radius': '3px',
    });
    cssTest('border-color: red', {
      'border-bottom-color': 'red',
      'border-color': 'red',
      'border-left-color': 'red',
      'border-right-color': 'red',
      'border-top-color': 'red',
    });
    cssTest(
      'border-top: inset blue 2px; border: 3px solid red',
      {
        border: '3px solid red',
        'border-top': '3px solid red',
        'border-top-color': 'red',
        'border-top-style': 'solid',
        'border-top-width': '3px',
        'border-right': '3px solid red',
        'border-right-color': 'red',
        'border-right-style': 'solid',
        'border-right-width': '3px',
        'border-bottom': '3px solid red',
        'border-bottom-color': 'red',
        'border-bottom-style': 'solid',
        'border-bottom-width': '3px',
        'border-left': '3px solid red',
        'border-left-color': 'red',
        'border-left-style': 'solid',
        'border-left-width': '3px',
      },
      'border: 3px solid red;'
    );
    cssTest(
      'border-top-color: blue; border-top: 3px solid red',
      {
        'border-top': '3px solid red',
        'border-top-color': 'red',
        'border-top-style': 'solid',
        'border-top-width': '3px',
      },
      'border-top: 3px solid red;'
    );
  });

  test('background', () => {
    cssTest('background: red; background-clip: none', {
      background: 'red',
      'background-clip': 'none',
    });
  });

  test('font', () => {
    cssFontTest('font-family: arial', { 'font-family': 'arial' });
    cssFontTest('font-size: 120%', { 'font-size': '120%' });
    cssFontTest('font-style: italic', { 'font-style': 'italic' });
    cssFontTest('font-weight: bold', { 'font-weight': 'bold' });
    cssFontTest('font: arial 120%', {
      'font-size': '120%',
      'font-family': 'arial',
    });
    cssFontTest('font:   arial   120%', {
      'font-size': '120%',
      'font-family': 'arial',
    });
    cssFontTest('font: 120% arial', {
      'font-size': '120%',
      'font-family': 'arial',
    });
    cssFontTest('font: arial small', {
      'font-size': 'small',
      'font-family': 'arial',
    });
    cssFontTest('font: arial 16px', {
      'font-size': '16px',
      'font-family': 'arial',
    });
    cssFontTest('font: arial bold 120%', {
      'font-weight': 'bold',
      'font-size': '120%',
      'font-family': 'arial',
    });
    cssFontTest('font: arial lighter 120%', {
      'font-weight': 'lighter',
      'font-size': '120%',
      'font-family': 'arial',
    });
    cssFontTest('font: arial italic 120%', {
      'font-style': 'italic',
      'font-size': '120%',
      'font-family': 'arial',
    });
    cssFontTest('font: arial bold italic 120%', {
      'font-style': 'italic',
      'font-weight': 'bold',
      'font-size': '120%',
      'font-family': 'arial',
    });
    cssFontTest('font: arial full-width 120%', {
      'font-variant': 'full-width',
      'font-size': '120%',
      'font-family': 'arial',
    });
    cssFontTest('font: arial full-width simplified 120%', {
      'font-variant': 'full-width simplified',
      'font-size': '120%',
      'font-family': 'arial',
    });
    cssFontTest('font: arial condensed 120%', {
      'font-stretch': 'condensed',
      'font-size': '120%',
      'font-family': 'arial',
    });
    cssFontTest('font: arial semi-condensed 120%', {
      'font-stretch': 'semi-condensed',
      'font-size': '120%',
      'font-family': 'arial',
    });
    cssFontTest('font: arial small/.75', {
      'line-height': '.75',
      'font-size': 'small',
      'font-family': 'arial',
    });
    cssFontTest('font: arial small/12px', {
      'line-height': '12px',
      'font-size': 'small',
      'font-family': 'arial',
    });
    cssFontTest('font: arial small/75%', {
      'line-height': '75%',
      'font-size': 'small',
      'font-family': 'arial',
    });
    cssFontTest('font: arial bold 120%; font-style: italic', {
      'font-weight': 'bold',
      'font-size': '120%',
      'font-family': 'arial',
      'font-style': 'italic',
    });
    cssFontTest('font-size-adjust: none', {
      'font-size-adjust': 'none',
    });
  });

  test('get()', () => {
    const styles = new Styles('padding: 0; padding-left: 1px');
    expect(styles.get('padding')).toBe('0 0 0 1px');
    expect(styles.get('padding-top')).toBe('0');
    expect(styles.get('padding-right')).toBe('0');
    expect(styles.get('padding-bottom')).toBe('0');
    expect(styles.get('padding-left')).toBe('1px');
    expect(styles.get('border')).toBe('');
  });

  test('set()', () => {
    const styles = new Styles('padding-left: 2px');
    styles.set('padding-left', '3px');
    expect(styles.get('padding-left')).toBe('3px');
    expect(styles.get('padding')).toBe('');
    expect(styles.cssText).toBe('padding-left: 3px;');
    styles.set('padding', '');
    expect(styles.get('padding-left')).toBe('');
    expect(styles.get('padding')).toBe('');
    expect(styles.cssText).toBe('');
  });
});
