import 'dart:convert';

import 'package:flutter_test/flutter_test.dart';
import 'package:marianum_mobile/api/marianumcloud/talk/create_poll/create_poll_params.dart';
import 'package:marianum_mobile/api/marianumcloud/talk/get_poll/get_poll_state_response.dart';
import 'package:marianum_mobile/api/marianumcloud/talk/vote_poll/vote_poll_params.dart';

GetPollStateResponseObject _poll({
  dynamic votes = const <String, dynamic>{},
  String actorType = 'users',
  String actorId = 'creator',
  int status = pollStatusOpen,
  int resultMode = pollResultModePublic,
  int maxVotes = 1,
  List<int> votedSelf = const [],
  int? numVoters,
}) => GetPollStateResponseObject(
  1,
  'Frage?',
  const ['A', 'B'],
  votes,
  actorType,
  actorId,
  'Creator',
  status,
  resultMode,
  maxVotes,
  votedSelf,
  numVoters,
  null,
);

void main() {
  group('GetPollStateResponseObject.voteCounts', () {
    test('map input is returned as num map', () {
      final poll = _poll(votes: {'option-0': 2, 'option-1': 5});
      expect(poll.voteCounts, {'option-0': 2, 'option-1': 5});
    });

    test('empty list (hidden results) becomes empty map', () {
      expect(_poll(votes: const []).voteCounts, isEmpty);
    });

    test('null votes becomes empty map', () {
      expect(_poll(votes: null).voteCounts, isEmpty);
    });

    test('non-num values are filtered out', () {
      final poll = _poll(votes: {'option-0': 3, 'option-1': 'x'});
      expect(poll.voteCounts, {'option-0': 3});
    });
  });

  group('GetPollStateResponseObject status/resultMode', () {
    test('isClosed reflects status', () {
      expect(_poll(status: pollStatusOpen).isClosed, isFalse);
      expect(_poll(status: pollStatusClosed).isClosed, isTrue);
    });

    test('resultsHidden reflects resultMode', () {
      expect(_poll(resultMode: pollResultModePublic).resultsHidden, isFalse);
      expect(_poll(resultMode: pollResultModeHidden).resultsHidden, isTrue);
    });
  });

  group('GetPollStateResponseObject.canClose', () {
    test('creator may close', () {
      final poll = _poll(actorId: 'me');
      expect(poll.canClose(selfId: 'me', participantType: 3), isTrue);
    });

    test('moderator types may close', () {
      final poll = _poll(actorId: 'someone');
      for (final type in [1, 2, 6]) {
        expect(
          poll.canClose(selfId: 'me', participantType: type),
          isTrue,
          reason: 'participantType $type should be allowed',
        );
      }
    });

    test('regular participant may not close', () {
      final poll = _poll(actorId: 'someone');
      expect(poll.canClose(selfId: 'me', participantType: 3), isFalse);
    });

    test('closed poll can never be closed again', () {
      final poll = _poll(actorId: 'me', status: pollStatusClosed);
      expect(poll.canClose(selfId: 'me', participantType: 1), isFalse);
    });

    test('guest with matching id is not treated as creator', () {
      final poll = _poll(actorType: 'guests', actorId: 'me');
      expect(poll.canClose(selfId: 'me', participantType: 3), isFalse);
    });
  });

  group('poll params serialization', () {
    // Regression: der ursprüngliche Bug entstand, weil optionIds nicht als
    // valides JSON serialisiert wurde und der Server ein leeres Array sah.
    test('VotePollParams encodes optionIds as a JSON array', () {
      final json = jsonEncode(VotePollParams(optionIds: [0, 2]).toJson());
      expect(json, '{"optionIds":[0,2]}');
    });

    test('empty VotePollParams encodes an empty array', () {
      final json = jsonEncode(VotePollParams(optionIds: []).toJson());
      expect(json, '{"optionIds":[]}');
    });

    test('CreatePollParams encodes all fields', () {
      final json = jsonEncode(
        CreatePollParams(
          question: 'Q?',
          options: const ['A', 'B'],
          resultMode: pollResultModeHidden,
          maxVotes: 0,
        ).toJson(),
      );
      expect(
        json,
        '{"question":"Q?","options":["A","B"],"resultMode":1,"maxVotes":0}',
      );
    });
  });
}
