import 'package:flutter_test/flutter_test.dart';
import 'package:ocideck/models/chart.dart';

void main() {
  test('chart palette starts with the EU flag colors', () {
    expect(chartColorPalette.take(2), ['#003399', '#FFCC00']);
  });

  group('parseCsv', () {
    test('reads header series names and labelled rows', () {
      final (x, series) = parseCsv('\n, 2025, 2026\nQ1, 10, 12\nQ2, 14, 9\n');
      expect(x, ['Q1', 'Q2']);
      expect(series.map((s) => s.name), ['2025', '2026']);
      expect(series[0].data, [10, 14]);
      expect(series[1].data, [12, 9]);
    });

    test('non-numeric cells become 0', () {
      final (x, series) = parseCsv(',A\nQ1,oops');
      expect(x, ['Q1']);
      expect(series.single.data, [0]);
    });
  });

  group('ChartSpec', () {
    test('round-trips inline data through the block JSON', () {
      const spec = ChartSpec(
        type: ChartType.line,
        title: 'Omzet',
        x: ['Q1', 'Q2'],
        rowColors: ['#003399', '#FFCC00'],
        series: [
          ChartSeries(name: '2025', data: [10, 14], color: '#EF4444'),
        ],
      );
      final back = ChartSpec.parse(spec.toBlock());
      expect(back.type, ChartType.line);
      expect(back.title, 'Omzet');
      expect(back.x, ['Q1', 'Q2']);
      expect(back.rowColors, ['#003399', '#FFCC00']);
      expect(back.series.single.name, '2025');
      expect(back.series.single.data, [10, 14]);
      expect(back.series.single.color, '#EF4444');
      expect(back.hasInlineData, isTrue);
    });

    test('storage form drops inline data when a source is linked', () {
      const spec = ChartSpec(
        type: ChartType.bar,
        title: 'Omzet',
        source: 'data/omzet.csv',
        x: ['Q1', 'Q2'],
        rowColors: ['#003399', '#FFCC00'],
        series: [
          ChartSeries(name: '2025', data: [10, 14], color: '#10B981'),
        ],
      );
      final stored = ChartSpec.parse(spec.toBlock(forStorage: true));
      expect(stored.source, 'data/omzet.csv');
      expect(stored.hasInlineData, isFalse);
      expect(stored.rowColors, ['#003399', '#FFCC00']);
      expect(stored.series.single.color, '#10B981');

      // The in-app/full form keeps the data.
      final full = ChartSpec.parse(spec.toBlock());
      expect(full.hasInlineData, isTrue);
    });

    test('withCsv fills x/series and keeps the source', () {
      const spec = ChartSpec(
        type: ChartType.bar,
        source: 'data/o.csv',
        rowColors: ['#003399', '#FFCC00'],
        series: [ChartSeries(name: 'oud', data: [], color: '#10B981')],
      );
      final filled = spec.withCsv(',A,B\nJan,1,2\nFeb,3,4');
      expect(filled.source, 'data/o.csv');
      expect(filled.x, ['Jan', 'Feb']);
      expect(filled.series.map((s) => s.name), ['A', 'B']);
      expect(filled.series[1].data, [2, 4]);
      expect(filled.series[0].color, '#10B981');
      expect(filled.series[1].color, isNull);
      expect(filled.rowColors, ['#003399', '#FFCC00']);
    });

    test('invalid colors are ignored while valid colors are normalized', () {
      final valid = ChartSeries.fromJson({
        'name': 'A',
        'data': [1],
        'color': 'ef4444',
      });
      final invalid = ChartSeries.fromJson({
        'name': 'B',
        'data': [2],
        'color': 'red',
      });
      expect(valid.color, '#EF4444');
      expect(invalid.color, isNull);
    });

    test('parse is tolerant of malformed JSON', () {
      final spec = ChartSpec.parse('{ not json');
      expect(spec.type, ChartType.bar);
      expect(spec.hasInlineData, isFalse);
    });

    test('round-trips optional min/max bound lines for bar/line', () {
      const spec = ChartSpec(
        type: ChartType.line,
        x: ['Q1'],
        series: [ChartSeries(name: 'A', data: [10])],
        minBound: 5,
        maxBound: 20,
      );
      final back = ChartSpec.parse(spec.toBlock());
      expect(back.minBound, 5);
      expect(back.maxBound, 20);
    });

    test('bounds are dropped from a pie chart', () {
      const spec = ChartSpec(
        type: ChartType.pie,
        x: ['Q1'],
        series: [ChartSeries(name: 'A', data: [10])],
        minBound: 5,
        maxBound: 20,
      );
      expect(spec.supportsBounds, isFalse);
      final back = ChartSpec.parse(spec.toBlock());
      expect(back.minBound, isNull);
      expect(back.maxBound, isNull);
    });

    test('round-trips a spider/radar chart type', () {
      const spec = ChartSpec(
        type: ChartType.radar,
        x: ['Snelheid', 'Kracht', 'Uithouding'],
        series: [
          ChartSeries(name: 'A', data: [3, 4, 5]),
        ],
      );
      final back = ChartSpec.parse(spec.toBlock());
      expect(back.type, ChartType.radar);
      expect(back.x, ['Snelheid', 'Kracht', 'Uithouding']);
      expect(back.series.single.data, [3, 4, 5]);
    });

    test('radar keeps bounds as a scale but never draws bound lines', () {
      const spec = ChartSpec(
        type: ChartType.radar,
        x: ['A', 'B', 'C'],
        series: [ChartSeries(name: 'A', data: [1, 2, 3])],
        minBound: 1,
        maxBound: 5,
      );
      expect(spec.supportsBounds, isTrue);
      expect(spec.supportsBoundLines, isFalse);
      final back = ChartSpec.parse(spec.toBlock());
      expect(back.minBound, 1);
      expect(back.maxBound, 5);
    });

    test('bar/line draw bound lines but pie does not', () {
      const bar = ChartSpec(type: ChartType.bar);
      const line = ChartSpec(type: ChartType.line);
      const pie = ChartSpec(type: ChartType.pie);
      expect(bar.supportsBoundLines, isTrue);
      expect(line.supportsBoundLines, isTrue);
      expect(pie.supportsBoundLines, isFalse);
      expect(pie.supportsBounds, isFalse);
    });
  });
}
