import 'package:logging/logging.dart';
import 'package:relatica/services/auth_service.dart';
import 'package:relatica/services/reshared_via_service.dart';

import '../../globals.dart';
import '../../models/engagement_summary.dart';
import '../../models/link_data.dart';
import '../../models/location_data.dart';
import '../../models/timeline_entry.dart';
import '../../models/visibility.dart';
import '../../services/connections_manager.dart';
import '../../services/hashtag_service.dart';
import '../../utils/active_profile_selector.dart';
import '../../utils/dateutils.dart';
import 'connection_mastodon_extensions.dart';
import 'hashtag_mastodon_extensions.dart';
import 'link_preview_mastodon_extensions.dart';
import 'media_attachment_mastodon_extension.dart';

final _logger = Logger('TimelineEntryMastodonExtensions');

extension TimelineEntryMastodonExtensions on TimelineEntry {
  static TimelineEntry fromJson(Map<String, dynamic> json) {
    final activeProfile = getIt<AccountsService>().currentProfile;
    final resharedViaService = getIt<ActiveProfileSelector<ReshareViaService>>()
        .getForProfile(activeProfile)
        .fold(
            onSuccess: (s) => s,
            onError: (error) {
              _logger.severe('Error getting reshared via service: $error');
              return null;
            });

    final connectionManager = getIt<ActiveProfileSelector<ConnectionsManager>>()
        .getForProfile(activeProfile)
        .fold(
            onSuccess: (m) => m,
            onError: (error) {
              _logger.severe('Error getting connection manager: $error');
              return null;
            });

    final id = json['id'] ?? '';
    final parentId = json['in_reply_to_id'] ?? '';
    final parentAuthor = json['in_reply_to_account_id'] ?? '';
    final parentAuthorId = json['in_reply_to_account_id'] ?? '';
    final author = json['account']['display_name'];
    final authorId = json['account']['id'];
    final resharePostData = json['reblog'];

    if (resharePostData != null) {
      final rebloggedUser =
          ConnectionMastodonExtensions.fromJson(resharePostData['account']);
      connectionManager?.upsertConnection(rebloggedUser);
      final resharedPost = fromJson(resharePostData);
      resharedViaService?.upsertResharedVia(
        postId: resharedPost.id,
        resharerId: authorId,
      );
      return resharedPost;
    }

    final int timestamp = json.containsKey('created_at')
        ? OffsetDateTimeUtils.epochSecTimeFromTimeZoneString(json['created_at'])
            .fold(
                onSuccess: (value) => value,
                onError: (error) {
                  _logger.severe("Couldn't read date time string: $error");
                  return 0;
                })
        : 0;

    final youReshared = json['reblogged'] ?? false;
    late final Visibility visibility;
    final visibilityString = json['visibility'];
    if (visibilityString == 'public') {
      visibility = Visibility.public();
    } else if (visibilityString == 'private') {
      final allowedUserIds =
          json['friendica']?['visibility']?['allow_cid'] as List<dynamic>? ??
              [];
      final excludedUserIds =
          json['friendica']?['visibility']?['deny_cid'] as List<dynamic>? ?? [];
      final allowedCircleIds =
          json['friendica']?['visibility']?['allow_gid'] as List<dynamic>? ??
              [];
      final excludedCircleIds =
          json['friendica']?['visibility']?['deny_gid'] as List<dynamic>? ?? [];
      visibility = Visibility(
        type: VisibilityType.private,
        allowedUserIds: allowedUserIds.map((e) => e.toString()).toList(),
        excludedUserIds: excludedUserIds.map((e) => e.toString()).toList(),
        allowedCircleIds: allowedCircleIds.map((e) => e.toString()).toList(),
        excludedCircleIds: excludedCircleIds.map((e) => e.toString()).toList(),
      );
    } else if (visibilityString == 'unlisted') {
      visibility = Visibility.private();
    } else {
      visibility = Visibility.private();
    }

    const title = '';
    final body = json['content'] ?? '';
    final spoilerText = json['spoiler_text'] ?? '';
    final externalLink = json['uri'] ?? '';
    const actualLocationData = LocationData();

    final modificationTimestamp = timestamp;
    final backdatedTimestamp = timestamp;
    final isFavorited = json['favourited'] ?? false;
    final linkData = json['card'] == null
        ? <LinkData>[]
        : [LinkData.fromMastodonJson(json['card'])];
    final mediaAttachments = (json['media_attachments'] as List<dynamic>? ?? [])
        .map((json) =>
            MediaAttachmentMastodonExtension.fromJson(json, visibility))
        .toList();
    final favoritesCount = json['favourites_count'] ?? 0;
    final repliesCount = json['replies_count'] ?? 0;
    final rebloggedCount = json['reblogs_count'] ?? 0;
    final engagementSummary = EngagementSummary(
      favoritesCount: favoritesCount,
      rebloggedCount: rebloggedCount,
      repliesCount: repliesCount,
    );
    final linkPreviewData = LinkPreviewMastodonExtensions.fromJson(
      json['card'],
    );
    final List<dynamic>? tagsJson = json['tags'];
    final tags = <String>[];
    if (tagsJson?.isNotEmpty ?? false) {
      final tagManager = getIt<HashtagService>();
      for (final tagJson in tagsJson!) {
        final tag = HashtagMastodonExtensions.fromJson(tagJson);
        tags.add(tag.tag);
        tagManager.add(tag);
      }
    }

    final connection = ConnectionMastodonExtensions.fromJson(json['account']);
    connectionManager?.upsertConnection(connection);

    return TimelineEntry(
      creationTimestamp: timestamp,
      modificationTimestamp: modificationTimestamp,
      backdatedTimestamp: backdatedTimestamp,
      locationData: actualLocationData,
      spoilerText: spoilerText,
      body: body,
      youReshared: youReshared,
      visibility: visibility,
      id: id,
      parentId: parentId,
      parentAuthorId: parentAuthorId,
      isFavorited: isFavorited,
      externalLink: externalLink,
      author: author,
      authorId: authorId,
      parentAuthor: parentAuthor,
      title: title,
      links: linkData,
      tags: tags,
      mediaAttachments: mediaAttachments,
      engagementSummary: engagementSummary,
      linkPreviewData: linkPreviewData,
    );
  }
}
