import 'package:json_annotation/json_annotation.dart';

import '../../../../extensions/date_time.dart';
import '../../../api_response.dart';
import '../room/get_room_response.dart';

part 'get_chat_response.g.dart';

@JsonSerializable(explicitToJson: true)
class GetChatResponse extends ApiResponse {
  Set<GetChatResponseObject> data;

  GetChatResponse(this.data);

  factory GetChatResponse.fromJson(Map<String, dynamic> json) =>
      _$GetChatResponseFromJson(json);
  Map<String, dynamic> toJson() => _$GetChatResponseToJson(this);

  List<GetChatResponseObject> sortByTimestamp() {
    var sorted = data.toList();
    sorted.sort((a, b) => a.timestamp.compareTo(b.timestamp));
    return sorted;
  }
}

@JsonSerializable(explicitToJson: true)
class GetChatResponseObject {
  int id;
  String token;
  GetRoomResponseObjectMessageActorType actorType;
  String actorId;
  String actorDisplayName;
  int timestamp;
  String systemMessage;
  GetRoomResponseObjectMessageType messageType;
  bool isReplyable;
  String referenceId;
  String message;
  Map<String, int>? reactions;
  List<String>? reactionsSelf;
  @JsonKey(fromJson: _fromJson)
  Map<String, RichObjectString>? messageParameters;
  GetChatResponseObject? parent;

  /// Whether the server flagged this message to be rendered as Markdown
  /// (capability `markdown-messages`). Absent on older servers/messages, in
  /// which case it stays `false` and the body renders as plain text.
  bool markdown;

  GetChatResponseObject(
    this.id,
    this.token,
    this.actorType,
    this.actorId,
    this.actorDisplayName,
    this.timestamp,
    this.systemMessage,
    this.messageType,
    this.isReplyable,
    this.referenceId,
    this.message,
    this.messageParameters,
    this.reactions,
    this.reactionsSelf,
    this.parent, [
    this.markdown = false,
  ]);

  factory GetChatResponseObject.fromJson(Map<String, dynamic> json) =>
      _$GetChatResponseObjectFromJson(json);
  Map<String, dynamic> toJson() => _$GetChatResponseObjectToJson(this);

  static GetChatResponseObject getDateDummy(int timestamp) {
    var elementDate = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
    return getTextDummy(elementDate.formatDateRelativeShort());
  }

  static GetChatResponseObject getTextDummy(String text) =>
      GetChatResponseObject(
        0,
        '',
        GetRoomResponseObjectMessageActorType.user,
        '',
        '',
        0,
        '',
        GetRoomResponseObjectMessageType.system,
        false,
        '',
        text,
        null,
        null,
        null,
        null,
      );
}

Map<String, RichObjectString>? _fromJson(dynamic json) {
  if (json is Map<String, dynamic>) {
    final data = <String, RichObjectString>{};
    for (final element in json.keys) {
      data.putIfAbsent(
        element,
        () => RichObjectString.fromJson(json[element] as Map<String, dynamic>),
      );
    }
    return data;
  }

  return null;
}

@JsonSerializable(explicitToJson: true)
class RichObjectString {
  RichObjectStringObjectType type;
  String id;
  String name;

  String? path;
  String? link;

  RichObjectString(this.type, this.id, this.name, this.path, this.link);

  factory RichObjectString.fromJson(Map<String, dynamic> json) =>
      _$RichObjectStringFromJson(json);
  Map<String, dynamic> toJson() => _$RichObjectStringToJson(this);
}

enum RichObjectStringObjectType {
  @JsonValue('user')
  user,
  @JsonValue('group')
  group,
  @JsonValue('file')
  file,
  @JsonValue('guest')
  guest,
  @JsonValue('highlight')
  highlight,
  @JsonValue('talk-poll')
  talkPoll,
  @JsonValue('geo-location')
  geoLocation,
  @JsonValue('call')
  call,
}
