import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_localizations/stream_chat_localizations.dart';

class _NnStreamChatLocalizationsDelegate
    extends LocalizationsDelegate<StreamChatLocalizations> {
  const _NnStreamChatLocalizationsDelegate();

  @override
  bool isSupported(Locale locale) => locale.languageCode == 'nn';

  @override
  Future<StreamChatLocalizations> load(Locale locale) =>
      SynchronousFuture(const NnStreamChatLocalizations());

  @override
  bool shouldReload(_NnStreamChatLocalizationsDelegate old) => false;
}

/// A custom set of localizations for the 'nn' locale. In this example, only
/// the value for launchUrlError was modified to use a custom message as
/// an example. Everything else uses the American English (en_US) messages
/// and formatting.
class NnStreamChatLocalizations extends GlobalStreamChatLocalizations {
  /// Create an instance of the translation bundle for English.
  const NnStreamChatLocalizations({super.localeName = 'nn'});

  /// A [LocalizationsDelegate] for [NnStreamChatLocalizations].
  static const delegate = _NnStreamChatLocalizationsDelegate();

  @override
  String get launchUrlError => 'Custom error';

  @override
  String get loadingUsersError => 'Error loading users';

  @override
  String get noUsersLabel => 'There are no users currently';

  @override
  String get noPhotoOrVideoLabel => 'There is no photo or video';

  @override
  String get retryLabel => 'Retry';

  @override
  String get userLastOnlineText => 'Last online';

  @override
  String get userOnlineText => 'Online';

  @override
  String userTypingText(Iterable<User> users) {
    if (users.isEmpty) return '';
    final first = users.first;
    if (users.length == 1) {
      return '${first.name} is typing';
    }
    return '${first.name} and ${users.length - 1} more are typing';
  }

  @override
  String get threadReplyLabel => 'Thread Reply';

  @override
  String get onlyVisibleToYouText => 'Only visible to you';

  @override
  String threadReplyCountText(int count) => '$count Thread Replies';

  @override
  String attachmentsUploadProgressText({
    required int remaining,
    required int total,
  }) =>
      'Uploading $remaining/$total ...';

  @override
  String pinnedByUserText({
    required User pinnedBy,
    required User currentUser,
  }) {
    final pinnedByCurrentUser = currentUser.id == pinnedBy.id;
    if (pinnedByCurrentUser) return 'Pinned by You';
    return 'Pinned by ${pinnedBy.name}';
  }

  @override
  String get sendMessagePermissionError =>
      "You don't have permission to send messages";

  @override
  String get emptyMessagesText => 'There are no messages currently';

  @override
  String get genericErrorText => 'Something went wrong';

  @override
  String get loadingMessagesError => 'Error loading messages';

  @override
  String resultCountText(int count) => '$count results';

  @override
  String get messageDeletedText => 'This message is deleted.';

  @override
  String get messageDeletedLabel => 'Message deleted';

  @override
  String get editedMessageLabel => 'Edited';

  @override
  String get messageReactionsLabel => 'Message Reactions';

  @override
  String get emptyChatMessagesText => 'No chats here yet...';

  @override
  String threadSeparatorText(int replyCount) {
    if (replyCount == 1) return '1 Reply';
    return '$replyCount Replies';
  }

  @override
  String get connectedLabel => 'Connected';

  @override
  String get disconnectedLabel => 'Disconnected';

  @override
  String get reconnectingLabel => 'Reconnecting...';

  @override
  String get alsoSendAsDirectMessageLabel => 'Also send as direct message';

  @override
  String get addACommentOrSendLabel => 'Add a comment or send';

  @override
  String get searchGifLabel => 'Search GIFs';

  @override
  String get writeAMessageLabel => 'Write a message';

  @override
  String get instantCommandsLabel => 'Instant Commands';

  @override
  String fileTooLargeAfterCompressionError(double limitInMB) =>
      'The file is too large to upload. '
      'The file size limit is $limitInMB MB. '
      'We tried compressing it, but it was not enough.';

  @override
  String fileTooLargeError(double limitInMB) =>
      'The file is too large to upload. The file size limit is $limitInMB MB.';

  @override
  String get couldNotReadBytesFromFileError =>
      'Could not read bytes from file.';

  @override
  String get addAFileLabel => 'Add a file';

  @override
  String get photoFromCameraLabel => 'Photo from camera';

  @override
  String get uploadAFileLabel => 'Upload a file';

  @override
  String get uploadAPhotoLabel => 'Upload a photo';

  @override
  String get uploadAVideoLabel => 'Upload a video';

  @override
  String get videoFromCameraLabel => 'Video from camera';

  @override
  String get okLabel => 'OK';

  @override
  String get somethingWentWrongError => 'Something went wrong';

  @override
  String get addMoreFilesLabel => 'Add more files';

  @override
  String get enablePhotoAndVideoAccessMessage =>
      'Please enable access to your photos'
      '\nand videos so you can share them with friends.';

  @override
  String get allowGalleryAccessMessage => 'Allow access to your gallery';

  @override
  String get flagMessageLabel => 'Flag Message';

  @override
  String get flagMessageQuestion =>
      'Do you want to send a copy of this message to a'
      '\nmoderator for further investigation?';

  @override
  String get flagLabel => 'FLAG';

  @override
  String get cancelLabel => 'CANCEL';

  @override
  String get flagMessageSuccessfulLabel => 'Message flagged';

  @override
  String get flagMessageSuccessfulText =>
      'The message has been reported to a moderator.';

  @override
  String get deleteLabel => 'DELETE';

  @override
  String get deleteMessageLabel => 'Delete Message';

  @override
  String get deleteMessageQuestion =>
      'Are you sure you want to permanently delete this\nmessage?';

  @override
  String get operationCouldNotBeCompletedText =>
      "The operation couldn't be completed.";

  @override
  String get replyLabel => 'Reply';

  @override
  String togglePinUnpinText({required bool pinned}) {
    if (pinned) return 'Unpin from Conversation';
    return 'Pin to Conversation';
  }

  @override
  String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) {
    if (isDeleteFailed) return 'Retry Deleting Message';
    return 'Delete Message';
  }

  @override
  String get copyMessageLabel => 'Copy Message';

  @override
  String get editMessageLabel => 'Edit Message';

  @override
  String toggleResendOrResendEditedMessage({required bool isUpdateFailed}) {
    if (isUpdateFailed) return 'Resend Edited Message';
    return 'Resend';
  }

  @override
  String get photosLabel => 'Photos';

  String _getDay(DateTime dateTime) {
    final now = DateTime.now();
    final today = DateTime(now.year, now.month, now.day);
    final yesterday = DateTime(now.year, now.month, now.day - 1);

    final date = DateTime(dateTime.year, dateTime.month, dateTime.day);

    if (date == today) {
      return 'today';
    } else if (date == yesterday) {
      return 'yesterday';
    } else {
      return 'on ${Jiffy.parseFromDateTime(date).MMMd}';
    }
  }

  @override
  String sentAtText({required DateTime date, required DateTime time}) {
    final atTime = Jiffy.parseFromDateTime(time.toLocal());
    return 'Sent ${_getDay(date)} at ${atTime.jm}';
  }

  @override
  String get todayLabel => 'Today';

  @override
  String get yesterdayLabel => 'Yesterday';

  @override
  String get channelIsMutedText => 'Channel is muted';

  @override
  String get noTitleText => 'No title';

  @override
  String get letsStartChattingLabel => 'Let’s start chatting!';

  @override
  String get sendingFirstMessageLabel =>
      'How about sending your first message to a friend?';

  @override
  String get startAChatLabel => 'Start a chat';

  @override
  String get loadingChannelsError => 'Error loading channels';

  @override
  String get deleteConversationLabel => 'Delete Conversation';

  @override
  String get deleteConversationQuestion =>
      'Are you sure you want to delete this conversation?';

  @override
  String get streamChatLabel => 'Stream Chat';

  @override
  String get searchingForNetworkText => 'Searching for Network';

  @override
  String get offlineLabel => 'Offline...';

  @override
  String get tryAgainLabel => 'Try Again';

  @override
  String membersCountText(int count) {
    if (count == 1) return '1 Member';
    return '$count Members';
  }

  @override
  String watchersCountText(int count) {
    if (count == 1) return '1 Online';
    return '$count Online';
  }

  @override
  String get viewInfoLabel => 'View Info';

  @override
  String get leaveGroupLabel => 'Leave Group';

  @override
  String get leaveLabel => 'LEAVE';

  @override
  String get leaveConversationLabel => 'Leave conversation';

  @override
  String get leaveConversationQuestion =>
      'Are you sure you want to leave this conversation?';

  @override
  String get showInChatLabel => 'Show in Chat';

  @override
  String get saveImageLabel => 'Save Image';

  @override
  String get saveVideoLabel => 'Save Video';

  @override
  String get uploadErrorLabel => 'UPLOAD ERROR';

  @override
  String get giphyLabel => 'Giphy';

  @override
  String get shuffleLabel => 'Shuffle';

  @override
  String get sendLabel => 'Send';

  @override
  String get withText => 'with';

  @override
  String get inText => 'in';

  @override
  String get youText => 'You';

  @override
  String galleryPaginationText({
    required int currentPage,
    required int totalPages,
  }) =>
      '$currentPage of $totalPages';

  @override
  String get fileText => 'File';

  @override
  String get replyToMessageLabel => 'Reply to Message';

  @override
  String attachmentLimitExceedError(int limit) =>
      'Attachment limit exceeded, limit: $limit';

  @override
  String get slowModeOnLabel => 'Slow mode ON';

  @override
  String get downloadLabel => 'Download';

  @override
  String toggleMuteUnmuteUserText({required bool isMuted}) {
    if (isMuted) {
      return 'Unmute User';
    } else {
      return 'Mute User';
    }
  }

  @override
  String toggleMuteUnmuteGroupQuestion({required bool isMuted}) {
    if (isMuted) {
      return 'Are you sure you want to unmute this group?';
    } else {
      return 'Are you sure you want to mute this group?';
    }
  }

  @override
  String toggleMuteUnmuteUserQuestion({required bool isMuted}) {
    if (isMuted) {
      return 'Are you sure you want to unmute this user?';
    } else {
      return 'Are you sure you want to mute this user?';
    }
  }

  @override
  String toggleMuteUnmuteAction({required bool isMuted}) {
    if (isMuted) {
      return 'UNMUTE';
    } else {
      return 'MUTE';
    }
  }

  @override
  String toggleMuteUnmuteGroupText({required bool isMuted}) {
    if (isMuted) {
      return 'Unmute Group';
    } else {
      return 'Mute Group';
    }
  }

  @override
  String get linkDisabledDetails =>
      'Sending links is not allowed in this conversation.';

  @override
  String get linkDisabledError => 'Links are disabled';

  @override
  String get viewLibrary => 'View library';

  @override
  String unreadMessagesSeparatorText() => 'New messages';

  @override
  String get enableFileAccessMessage => 'Enable file access to continue';

  @override
  String get allowFileAccessMessage => 'Allow access to files';

  @override
  String get markAsUnreadLabel => 'Mark as unread';

  @override
  String unreadCountIndicatorLabel({required int unreadCount}) {
    return '$unreadCount unread';
  }

  @override
  String get markUnreadError =>
      'Error marking message unread. Cannot mark unread messages older than'
      ' the newest 100 channel messages.';

  @override
  String createPollLabel({bool isNew = false}) {
    if (isNew) return 'Create a new poll';
    return 'Create Poll';
  }

  @override
  String get questionsLabel => 'Questions';

  @override
  String get askAQuestionLabel => 'Ask a question';

  @override
  String? pollQuestionValidationError(int length, Range<int> range) {
    final (:min, :max) = range;

    // Check if the question is too short.
    if (min != null && length < min) {
      return 'Question must be at least $min characters long';
    }

    // Check if the question is too long.
    if (max != null && length > max) {
      return 'Question must be at most $max characters long';
    }

    return null;
  }

  @override
  String optionLabel({bool isPlural = false}) {
    if (isPlural) return 'Options';
    return 'Option';
  }

  @override
  String get pollOptionEmptyError => 'Option cannot be empty';

  @override
  String get pollOptionDuplicateError => 'This is already an option';

  @override
  String get addAnOptionLabel => 'Add an option';

  @override
  String get multipleAnswersLabel => 'Multiple answers';

  @override
  String get maximumVotesPerPersonLabel => 'Maximum votes per person';

  @override
  String? maxVotesPerPersonValidationError(int votes, Range<int> range) {
    final (:min, :max) = range;

    if (min != null && votes < min) {
      return 'Vote count must be at least $min';
    }

    if (max != null && votes > max) {
      return 'Vote count must be at most $max';
    }

    return null;
  }

  @override
  String get anonymousPollLabel => 'Anonymous poll';

  @override
  String get pollOptionsLabel => 'Poll Options';

  @override
  String get suggestAnOptionLabel => 'Suggest an option';

  @override
  String get enterANewOptionLabel => 'Enter a new option';

  @override
  String get addACommentLabel => 'Add a comment';

  @override
  String get pollCommentsLabel => 'Poll Comments';

  @override
  String get updateYourCommentLabel => 'Update your comment';

  @override
  String get enterYourCommentLabel => 'Enter your comment';

  @override
  String get createLabel => 'Create';

  @override
  String pollVotingModeLabel(PollVotingMode votingMode) {
    return votingMode.when(
      disabled: () => 'Vote ended',
      unique: () => 'Select one',
      limited: (count) => 'Select up to $count',
      all: () => 'Select one or more',
    );
  }

  @override
  String seeAllOptionsLabel({int? count}) {
    if (count == null) return 'See all options';
    return 'See all $count options';
  }

  @override
  String get viewCommentsLabel => 'View Comments';

  @override
  String get viewResultsLabel => 'View Results';

  @override
  String get endVoteLabel => 'End Vote';

  @override
  String get pollResultsLabel => 'Poll Results';

  @override
  String showAllVotesLabel({int? count}) {
    if (count == null) return 'Show all votes';
    return 'Show all $count votes';
  }

  @override
  String voteCountLabel({int? count}) => switch (count) {
        null || < 1 => '0 votes',
        1 => '1 vote',
        _ => '$count votes',
      };

  @override
  String get noPollVotesLabel => 'There are no poll votes currently';

  @override
  String get loadingPollVotesError => 'Error loading poll votes';
}

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  /// Create a new instance of [StreamChatClient] passing the apikey obtained
  /// from your project dashboard.
  final client = StreamChatClient(
    's2dxdhpxd94g',
    logLevel: Level.INFO,
  );

  /// Set the current user and connect the websocket. In a production
  /// scenario, this should be done using a backend to generate a user token
  /// using our server SDK.
  ///
  /// Please see the following for more information:
  /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/
  await client.connectUser(
    User(id: 'super-band-9'),
    '''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''',
  );

  final channel = client.channel('messaging', id: 'godevs');

  await channel.watch();

  runApp(
    MyApp(
      client: client,
      channel: channel,
    ),
  );
}

/// Example application using Stream Chat Flutter widgets.
///
/// Stream Chat Flutter is a set of Flutter widgets which provide full chat
/// functionalities for building Flutter applications using Stream. If you'd
/// prefer using minimal wrapper widgets for your app, please see our other
/// package, `stream_chat_flutter_core`.
class MyApp extends StatelessWidget {
  /// Example using Stream's Flutter package.
  ///
  /// If you'd prefer using minimal wrapper widgets for your app, please see
  /// our other package, `stream_chat_flutter_core`.
  const MyApp({
    super.key,
    required this.client,
    required this.channel,
  });

  /// Instance of Stream Client.
  ///
  /// Stream's [StreamChatClient] can be used to connect to our servers and
  /// set the default user for the application. Performing these actions
  /// trigger a websocket connection allowing for real-time updates.
  final StreamChatClient client;

  /// Instance of the Channel
  final Channel channel;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.light(),
      darkTheme: ThemeData.dark(),
      // Add all the supported locales
      supportedLocales: const [
        Locale('en'),
        Locale('hi'),
        Locale('fr'),
        Locale('it'),
        Locale('es'),
        Locale('ja'),
        Locale('ko'),
        // Add support for additional 'nn' locale
        Locale('nn'),
      ],
      // Add overridden "NnStreamChatLocalizations.delegate" along with
      // "GlobalStreamChatLocalizations.delegates"
      localizationsDelegates: const [
        NnStreamChatLocalizations.delegate,
        ...GlobalStreamChatLocalizations.delegates,
      ],
      builder: (context, widget) => StreamChat(
        client: client,
        child: widget,
      ),
      home: StreamChannel(
        channel: channel,
        child: const ChannelPage(),
      ),
    );
  }
}

/// A list of messages sent in the current channel.
///
/// This is implemented using [StreamMessageListView],
///  a widget that provides query
/// functionalities fetching the messages from the api and showing them in a
/// listView.
class ChannelPage extends StatelessWidget {
  /// Creates the page that shows the list of messages
  const ChannelPage({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      appBar: StreamChannelHeader(),
      body: Column(
        children: <Widget>[
          Expanded(
            child: StreamMessageListView(),
          ),
          StreamMessageInput(),
        ],
      ),
    );
  }
}
