import 'dart:convert';

import 'package:logging/logging.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:stream_chat/src/api/responses.dart';
import 'package:stream_chat/src/db/offline_storage.dart';
import 'package:stream_chat/src/models/channel_model.dart';
import 'package:stream_chat/src/models/channel_state.dart';
import 'package:stream_chat/src/models/message.dart';

import 'client.dart';
import 'models/own_user.dart';

/// Utility class to handle and show notifications
class NotificationService {
  static Future<void> _handleNotification(
    Message message,
    ChannelModel channelModel,
    Client client,
  ) async {
    if (message != null && client.persistenceEnabled) {
      if (client?.state?.channels == null) {
        final sharedPreferences = await _getSharedPreferences();
        final userId = sharedPreferences.getString(KEY_USER_ID);

        final offlineStorage = OfflineStorage(userId, Logger('💽'));
        await offlineStorage.updateChannelState(
          ChannelState(
            channel: channelModel,
            messages: [message],
          ),
        );
        await offlineStorage.disconnect();
      } else {
        final channel = client.state.channels[channelModel.cid];
        channel.state.updateChannelState(
          ChannelState(
            channel: channelModel,
            messages: [message],
          ),
        );
      }
    }
  }

  static SharedPreferences _sharedPreferences;
  static Future<SharedPreferences> _getSharedPreferences() async {
    _sharedPreferences ??= await SharedPreferences.getInstance();
    return _sharedPreferences;
  }

  /// Gets the message using the client without storing it in the offline storage
  /// It returns an object containing the information about the message and the channel
  static Future<GetMessageResponse> getMessage(String messageId) async {
    final sharedPreferences = await _getSharedPreferences();
    final apiKey = sharedPreferences.getString(KEY_API_KEY);

    final client = Client(
      apiKey,
      persistenceEnabled: false,
    );
    final userId = sharedPreferences.getString(KEY_USER_ID);
    final token = sharedPreferences.getString(KEY_TOKEN);

    client.state.user = OwnUser(id: userId);
    client.token = token;

    final res = await client.getMessage(messageId);
    return res;
  }

  /// Stores the message in the offline storage
  static Future<void> storeMessage(GetMessageResponse messageResponse) async {
    final sharedPreferences = await _getSharedPreferences();
    final userId = sharedPreferences.getString(KEY_USER_ID);

    final offlineStorage = OfflineStorage(
      userId,
      Logger('💽'),
    );

    await offlineStorage.updateChannelState(ChannelState(
      messages: [messageResponse.message],
      channel: messageResponse.channel,
    ));

    await offlineStorage.disconnect();
  }

  /// Gets the message using the client and stores it in the offline storage
  /// It returns an object containing the information about the message and the channel
  static Future<GetMessageResponse> getAndStoreMessage(String messageId) async {
    final getMessageResponse = await getMessage(messageId);
    await storeMessage(getMessageResponse);
    return getMessageResponse;
  }

  /// Handles the ios message queue generated by the Notification Service Extension
  static Future<void> handleIosMessageQueue(Client client) async {
    final sharedPreferences = await SharedPreferences.getInstance();
    await sharedPreferences.reload();
    final messageQueue = sharedPreferences.getStringList('messageQueue');
    if (messageQueue != null) {
      messageQueue.forEach((m) {
        final jsonMessage = jsonDecode(m);

        final message = Message.fromJson(jsonMessage);
        final channelModel = ChannelModel.fromJson(jsonMessage['channel']);

        _handleNotification(
          message,
          channelModel,
          client,
        );
      });
      await sharedPreferences.remove('messageQueue');
    }
  }
}
