// Copyright 2018 Google Inc. Use of this source code is governed by an
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

@TestOn('vm')
library;

import 'package:test/test.dart';

import 'package:sass/sass.dart';

import 'utils.dart';

void main() {
  group("a comma-separated list", () {
    late Value value;
    setUp(() => value = parseValue("a, b, c"));

    test("is comma-separated", () {
      expect(value.separator, equals(ListSeparator.comma));
    });

    test("has no brackets", () {
      expect(value.hasBrackets, isFalse);
    });

    test("returns its contents as a list", () {
      expect(
        value.asList,
        equals([
          SassString("a", quotes: false),
          SassString("b", quotes: false),
          SassString("c", quotes: false),
        ]),
      );
    });

    test("equals the same list", () {
      expect(
        value,
        equalsWithHash(
          SassList([
            SassString("a", quotes: false),
            SassString("b", quotes: false),
            SassString("c", quotes: false),
          ], ListSeparator.comma),
        ),
      );
    });

    test("doesn't equal a value with different metadata", () {
      expect(
        value,
        isNot(
          equals(
            SassList([
              SassString("a", quotes: false),
              SassString("b", quotes: false),
              SassString("c", quotes: false),
            ], ListSeparator.space),
          ),
        ),
      );

      expect(
        value,
        isNot(
          equals(
            SassList(
              [
                SassString("a", quotes: false),
                SassString("b", quotes: false),
                SassString("c", quotes: false),
              ],
              ListSeparator.comma,
              brackets: true,
            ),
          ),
        ),
      );
    });

    test("doesn't equal a value with different contents", () {
      expect(
        value,
        isNot(
          equals(
            SassList([
              SassString("a", quotes: false),
              SassString("x", quotes: false),
              SassString("c", quotes: false),
            ], ListSeparator.comma),
          ),
        ),
      );
    });

    group("sassIndexToListIndex()", () {
      test("converts a positive index to a Dart index", () {
        expect(value.sassIndexToListIndex(SassNumber(1)), equals(0));
        expect(value.sassIndexToListIndex(SassNumber(2)), equals(1));
        expect(value.sassIndexToListIndex(SassNumber(3)), equals(2));
      });

      test("converts a negative index to a Dart index", () {
        expect(value.sassIndexToListIndex(SassNumber(-1)), equals(2));
        expect(value.sassIndexToListIndex(SassNumber(-2)), equals(1));
        expect(value.sassIndexToListIndex(SassNumber(-3)), equals(0));
      });

      test("rejects a non-number", () {
        expect(
          () => value.sassIndexToListIndex(SassString("foo", quotes: false)),
          throwsSassScriptException,
        );
      });

      test("rejects a non-integer", () {
        expect(
          () => value.sassIndexToListIndex(SassNumber(1.1)),
          throwsSassScriptException,
        );
      });

      test("rejects invalid indices", () {
        expect(
          () => value.sassIndexToListIndex(SassNumber(0)),
          throwsSassScriptException,
        );
        expect(
          () => value.sassIndexToListIndex(SassNumber(4)),
          throwsSassScriptException,
        );
        expect(
          () => value.sassIndexToListIndex(SassNumber(-4)),
          throwsSassScriptException,
        );
      });
    });

    test("isn't any other type", () {
      expect(value.assertBoolean, throwsSassScriptException);
      expect(value.assertCalculation, throwsSassScriptException);
      expect(value.assertColor, throwsSassScriptException);
      expect(value.assertFunction, throwsSassScriptException);
      expect(value.assertMap, throwsSassScriptException);
      expect(value.tryMap(), isNull);
      expect(value.assertNumber, throwsSassScriptException);
      expect(value.assertString, throwsSassScriptException);
    });
  });

  test("a slash-separated list is slash-separated", () {
    expect(
      parseValue("list.slash(a, b, c)").separator,
      equals(ListSeparator.slash),
    );
  });

  test("a space-separated list is space-separated", () {
    expect(parseValue("a b c").separator, equals(ListSeparator.space));
  });

  test("a bracketed list has brackets", () {
    expect(parseValue("[a, b, c]").hasBrackets, isTrue);
  });

  group("a single-element list", () {
    late Value value;
    setUp(() => value = parseValue("[1]"));

    test("has an undecided separator", () {
      expect(value.separator, equals(ListSeparator.undecided));
    });

    test("returns its contents as a list", () {
      expect(value.asList, equals([SassNumber(1)]));
    });

    test("isn't any other type", () {
      expect(value.assertBoolean, throwsSassScriptException);
      expect(value.assertColor, throwsSassScriptException);
      expect(value.assertFunction, throwsSassScriptException);
      expect(value.assertMap, throwsSassScriptException);
      expect(value.tryMap(), isNull);
      expect(value.assertNumber, throwsSassScriptException);
      expect(value.assertString, throwsSassScriptException);
    });
  });

  test("a comma-separated single-element list is comma-separated", () {
    expect(parseValue("(1,)").separator, equals(ListSeparator.comma));
  });

  group("an empty list", () {
    late Value value;
    setUp(() => value = parseValue("()"));

    test("has an undecided separator", () {
      expect(value.separator, equals(ListSeparator.undecided));
    });

    test("returns its contents as a list", () {
      expect(value.asList, isEmpty);
    });

    test("equals an empty map", () {
      expect(value, equalsWithHash(SassMap.empty()));
    });

    test("counts as an empty map", () {
      expect(value.assertMap().contents, isEmpty);
      expect(value.tryMap()!.contents, isEmpty);
    });

    test("isn't any other type", () {
      expect(value.assertBoolean, throwsSassScriptException);
      expect(value.assertColor, throwsSassScriptException);
      expect(value.assertFunction, throwsSassScriptException);
      expect(value.assertNumber, throwsSassScriptException);
      expect(value.assertString, throwsSassScriptException);
    });

    test("sassIndexToListIndex() rejects invalid indices", () {
      expect(
        () => value.sassIndexToListIndex(SassNumber(0)),
        throwsSassScriptException,
      );
      expect(
        () => value.sassIndexToListIndex(SassNumber(1)),
        throwsSassScriptException,
      );
      expect(
        () => value.sassIndexToListIndex(SassNumber(-1)),
        throwsSassScriptException,
      );
    });
  });

  group("a scalar value", () {
    late Value value;
    setUp(() => value = parseValue("blue"));

    test("has an undecided separator", () {
      expect(value.separator, equals(ListSeparator.undecided));
    });

    test("has no brackets", () {
      expect(value.hasBrackets, isFalse);
    });

    test("returns itself as a list", () {
      var list = value.asList;
      expect(list, hasLength(1));
      expect(list.first, same(value));
    });

    group("sassIndexToListIndex()", () {
      test("converts a positive index to a Dart index", () {
        expect(value.sassIndexToListIndex(SassNumber(1)), equals(0));
      });

      test("converts a negative index to a Dart index", () {
        expect(value.sassIndexToListIndex(SassNumber(-1)), equals(0));
      });

      test("rejects invalid indices", () {
        expect(
          () => value.sassIndexToListIndex(SassNumber(0)),
          throwsSassScriptException,
        );
        expect(
          () => value.sassIndexToListIndex(SassNumber(2)),
          throwsSassScriptException,
        );
        expect(
          () => value.sassIndexToListIndex(SassNumber(-2)),
          throwsSassScriptException,
        );
      });
    });
  });

  group("new SassList.empty()", () {
    test("creates an empty list with default metadata", () {
      var list = SassList.empty();
      expect(list.asList, isEmpty);
      expect(list.separator, equals(ListSeparator.undecided));
      expect(list.hasBrackets, isFalse);
    });

    test("can set the metadata", () {
      var list = SassList.empty(separator: ListSeparator.space, brackets: true);
      expect(list.separator, equals(ListSeparator.space));
      expect(list.hasBrackets, isTrue);
    });
  });

  group("new SassList()", () {
    test("creates a list with the given contents and metadata", () {
      var list = SassList([
        SassString("a", quotes: false),
      ], ListSeparator.space);
      expect(list.asList, equals([SassString("a", quotes: false)]));
      expect(list.separator, equals(ListSeparator.space));
      expect(list.hasBrackets, isFalse);
    });

    test("can create a bracketed list", () {
      expect(
        SassList(
          [SassString("a", quotes: false)],
          ListSeparator.space,
          brackets: true,
        ).hasBrackets,
        isTrue,
      );
    });

    test("can create a short list with an undecided separator", () {
      expect(
        SassList([
          SassString("a", quotes: false),
        ], ListSeparator.undecided)
            .separator,
        equals(ListSeparator.undecided),
      );
      expect(
        SassList([], ListSeparator.undecided).separator,
        equals(ListSeparator.undecided),
      );
    });

    test("can't create a long list with an undecided separator", () {
      expect(
        () => SassList([
          SassString("a", quotes: false),
          SassString("b", quotes: false),
        ], ListSeparator.undecided),
        throwsArgumentError,
      );
    });
  });
}
