// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

@TestOn('vm')
library;

import 'dart:io';

import 'package:io/ansi.dart';
import 'package:test/test.dart';

const _ansiEscapeLiteral = '\x1B';
const _ansiEscapeForScript = r'\033';
const sampleInput = 'sample input';

void main() {
  group('ansiOutputEnabled', () {
    test('default value matches dart:io', () {
      expect(ansiOutputEnabled,
          stdout.supportsAnsiEscapes && stderr.supportsAnsiEscapes);
    });

    test('override true', () {
      overrideAnsiOutput(true, () {
        expect(ansiOutputEnabled, isTrue);
      });
    });

    test('override false', () {
      overrideAnsiOutput(false, () {
        expect(ansiOutputEnabled, isFalse);
      });
    });

    test('forScript variaents ignore `ansiOutputEnabled`', () {
      const expected =
          '$_ansiEscapeForScript[34m$sampleInput$_ansiEscapeForScript[0m';

      for (var override in [true, false]) {
        overrideAnsiOutput(override, () {
          expect(blue.escapeForScript, '$_ansiEscapeForScript[34m');
          expect(blue.wrap(sampleInput, forScript: true), expected);
          expect(wrapWith(sampleInput, [blue], forScript: true), expected);
        });
      }
    });
  });

  test('foreground and background colors match', () {
    expect(foregroundColors, hasLength(backgroundColors.length));

    for (var i = 0; i < foregroundColors.length; i++) {
      final foreground = foregroundColors[i];
      expect(foreground.type, AnsiCodeType.foreground);
      expect(foreground.name.toLowerCase(), foreground.name,
          reason: 'All names should be lower case');
      final background = backgroundColors[i];
      expect(background.type, AnsiCodeType.background);
      expect(background.name.toLowerCase(), background.name,
          reason: 'All names should be lower case');

      expect(foreground.name, background.name);

      // The last base-10 digit also matches – good to sanity check
      expect(foreground.code % 10, background.code % 10);
    }
  });

  test('all styles are styles', () {
    for (var style in styles) {
      expect(style.type, AnsiCodeType.style);
      expect(style.name.toLowerCase(), style.name,
          reason: 'All names should be lower case');
      if (style == styleBold) {
        expect(style.reset, resetBold);
      } else {
        expect(style.reset!.code, equals(style.code + 20));
      }
      expect(style.name, equals(style.reset!.name));
    }
  });

  for (var forScript in [true, false]) {
    group(forScript ? 'forScript' : 'escaped', () {
      final escapeLiteral =
          forScript ? _ansiEscapeForScript : _ansiEscapeLiteral;

      group('wrap', () {
        _test('color', () {
          final expected = '$escapeLiteral[34m$sampleInput$escapeLiteral[0m';

          expect(blue.wrap(sampleInput, forScript: forScript), expected);
        });

        _test('style', () {
          final expected = '$escapeLiteral[1m$sampleInput$escapeLiteral[22m';

          expect(styleBold.wrap(sampleInput, forScript: forScript), expected);
        });

        _test('style', () {
          final expected = '$escapeLiteral[34m$sampleInput$escapeLiteral[0m';

          expect(blue.wrap(sampleInput, forScript: forScript), expected);
        });

        test('empty', () {
          expect(blue.wrap('', forScript: forScript), '');
        });

        test(null, () {
          expect(blue.wrap(null, forScript: forScript), isNull);
        });
      });

      group('wrapWith', () {
        _test('foreground', () {
          final expected = '$escapeLiteral[34m$sampleInput$escapeLiteral[0m';

          expect(wrapWith(sampleInput, [blue], forScript: forScript), expected);
        });

        _test('background', () {
          final expected = '$escapeLiteral[44m$sampleInput$escapeLiteral[0m';

          expect(wrapWith(sampleInput, [backgroundBlue], forScript: forScript),
              expected);
        });

        _test('style', () {
          final expected = '$escapeLiteral[1m$sampleInput$escapeLiteral[0m';

          expect(wrapWith(sampleInput, [styleBold], forScript: forScript),
              expected);
        });

        _test('2 styles', () {
          final expected = '$escapeLiteral[1;3m$sampleInput$escapeLiteral[0m';

          expect(
              wrapWith(sampleInput, [styleBold, styleItalic],
                  forScript: forScript),
              expected);
        });

        _test('2 foregrounds', () {
          expect(
              () => wrapWith(sampleInput, [blue, white], forScript: forScript),
              throwsArgumentError);
        });

        _test('multi', () {
          final expected =
              '$escapeLiteral[1;4;34;107m$sampleInput$escapeLiteral[0m';

          expect(
              wrapWith(sampleInput,
                  [blue, backgroundWhite, styleBold, styleUnderlined],
                  forScript: forScript),
              expected);
        });

        test('no codes', () {
          expect(wrapWith(sampleInput, []), sampleInput);
        });

        _test('empty', () {
          expect(
              wrapWith('', [blue, backgroundWhite, styleBold],
                  forScript: forScript),
              '');
        });

        _test('null', () {
          expect(
              wrapWith(null, [blue, backgroundWhite, styleBold],
                  forScript: forScript),
              isNull);
        });
      });
    });
  }
}

void _test<T>(String name, T Function() body) =>
    test(name, () => overrideAnsiOutput<T>(true, body));
