import 'package:flutter_test/flutter_test.dart';
import 'package:qlyra/backend/models/chat_folder.dart';
import 'package:qlyra/backend/modules/chats.dart';
import 'package:qlyra/backend/modules/folders.dart';

const int _me = 4242;
const int _contactId = 777;

CachedChat _chat({
  required int id,
  required String type,
  int unreadCount = 0,
  int dontDisturbUntil = 0,
  Set<String> options = const {},
  int? owner,
}) => CachedChat(
  id: id,
  accountId: _me,
  type: type,
  title: 'chat $id',
  unreadCount: unreadCount,
  lastEventTime: 1700000000000,
  cachedAt: 0,
  dontDisturbUntil: dontDisturbUntil,
  isOnline: false,
  seenTime: 0,
  participants: {_me: 1700000000000},
  options: options,
  owner: owner,
);

bool _matches(CachedChat chat, ChatFolder folder) =>
    FoldersModule.chatMatchesFolder(
      chat,
      folder,
      myId: _me,
      contactIds: const {_contactId},
    );

void main() {
  group('ChatFolder.fromJson', () {
    test('reads the server payload of FOLDERS_UPDATE', () {
      final folder = ChatFolder.fromJson(const {
        'id': '6fd177c5-ba2e-4360-9593-3ae798326806',
        'title': 'Каналы',
        'include': [111, -222],
        'filters': [0, 11, 2],
        'favorites': [111],
        'options': [1, 2],
        'updateTime': 1700000000000,
        'sourceId': 1,
      });

      expect(folder.id, '6fd177c5-ba2e-4360-9593-3ae798326806');
      expect(folder.include, [111, -222]);
      expect(folder.filters, [
        FolderFilter.unread,
        FolderFilter.notMuted,
        FolderFilter.channel,
      ]);
      expect(folder.favorites, [111]);
      expect(folder.updateTime, 1700000000000);
      expect(folder.sourceId, 1);
      expect(folder.canDelete, isFalse);
      expect(folder.canEditTitle, isFalse);
      expect(folder.canEditFilters, isTrue);
    });

    test('normalizes legacy string filters and the hideEmpty flag', () {
      final folder = ChatFolder.fromJson(const {
        'id': 'legacy',
        'title': 'legacy',
        'filters': ['CHANNEL', 'GROUP'],
        'hideEmpty': true,
      });

      expect(folder.filters, [FolderFilter.channel, FolderFilter.chat]);
      expect(folder.options, [FolderOption.hideEmpty]);
      expect(folder.hideEmpty, isTrue);
    });
  });

  group('chatMatchesFolder', () {
    const channelsFolder = ChatFolder(
      id: 'f1',
      title: 'Каналы',
      filters: [FolderFilter.channel],
    );

    test('type filters are combined with OR', () {
      const folder = ChatFolder(
        id: 'f2',
        title: 'Каналы и боты',
        filters: [FolderFilter.channel, FolderFilter.bot],
      );

      expect(_matches(_chat(id: 1, type: 'CHANNEL'), folder), isTrue);
      expect(
        _matches(_chat(id: 2, type: 'DIALOG', options: {'BOT'}), folder),
        isTrue,
      );
      expect(_matches(_chat(id: 3, type: 'CHAT'), folder), isFalse);
    });

    test('a folder without filters only holds its included chats', () {
      const folder = ChatFolder(id: 'f3', title: 'Свои', include: [10]);

      expect(_matches(_chat(id: 10, type: 'CHAT'), folder), isTrue);
      expect(_matches(_chat(id: 11, type: 'CHAT'), folder), isFalse);
    });

    test('show-only filters narrow both types and included chats', () {
      const folder = ChatFolder(
        id: 'f4',
        title: 'Непрочитанные каналы',
        include: [10],
        filters: [FolderFilter.channel, FolderFilter.unread],
      );

      expect(
        _matches(_chat(id: 1, type: 'CHANNEL', unreadCount: 3), folder),
        isTrue,
      );
      expect(_matches(_chat(id: 2, type: 'CHANNEL'), folder), isFalse);
      expect(
        _matches(_chat(id: 10, type: 'CHAT', unreadCount: 1), folder),
        isTrue,
      );
      expect(_matches(_chat(id: 10, type: 'CHAT'), folder), isFalse);
    });

    test('show-only filters are combined with AND', () {
      const folder = ChatFolder(
        id: 'f5',
        title: 'Непрочитанные с уведомлениями',
        filters: [
          FolderFilter.channel,
          FolderFilter.unread,
          FolderFilter.notMuted,
        ],
      );

      expect(
        _matches(_chat(id: 1, type: 'CHANNEL', unreadCount: 1), folder),
        isTrue,
      );
      expect(
        _matches(
          _chat(id: 2, type: 'CHANNEL', unreadCount: 1, dontDisturbUntil: -1),
          folder,
        ),
        isFalse,
      );
    });

    test('an expired mute counts as not muted', () {
      const folder = ChatFolder(
        id: 'f6',
        title: 'С уведомлениями',
        filters: [FolderFilter.channel, FolderFilter.notMuted],
      );

      expect(
        _matches(
          _chat(id: 1, type: 'CHANNEL', dontDisturbUntil: 1700000000000),
          folder,
        ),
        isTrue,
      );
    });

    test('unknown filters do not empty a folder', () {
      const folder = ChatFolder(
        id: 'f7',
        title: 'Каналы',
        filters: [FolderFilter.channel, FolderFilter.markedUnread],
      );

      expect(_matches(_chat(id: 1, type: 'CHANNEL'), folder), isTrue);
    });

    test('role filters keep only chats with that role', () {
      const folder = ChatFolder(
        id: 'f8',
        title: 'Мои группы',
        filters: [FolderFilter.chat, FolderFilter.owner],
      );

      expect(_matches(_chat(id: 1, type: 'CHAT', owner: _me), folder), isTrue);
      expect(_matches(_chat(id: 2, type: 'CHAT', owner: 5), folder), isFalse);
    });

    test('channels folder ignores groups and dialogs', () {
      expect(_matches(_chat(id: 1, type: 'CHANNEL'), channelsFolder), isTrue);
      expect(_matches(_chat(id: 2, type: 'CHAT'), channelsFolder), isFalse);
      expect(_matches(_chat(id: 3, type: 'DIALOG'), channelsFolder), isFalse);
    });
  });

  group('newFolderId', () {
    test('generates distinct uuid v4 ids', () {
      final a = FoldersModule.newFolderId();
      final b = FoldersModule.newFolderId();

      expect(a, isNot(b));
      expect(
        RegExp(
          r'^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$',
        ).hasMatch(a),
        isTrue,
      );
    });
  });
}
