//
//  NotificationGroupTests.swift
//  
//
//  Created by Shadowfacts on 4/26/22.
//

import XCTest
@testable import Pachyderm

@MainActor
class NotificationGroupTests: XCTestCase {
    
    let decoder: JSONDecoder = {
        let d = JSONDecoder()
        d.dateDecodingStrategy = .iso8601
        return d
    }()
    
    let statusA = """
    {
        "id": "1",
        "created_at": "2019-11-23T07:28:34Z",
        "account": {
          "id": "2",
          "username": "bar",
          "acct": "bar",
          "display_name": "bar",
          "locked": false,
          "created_at": "2019-11-01T01:01:01Z",
          "followers_count": 0,
          "following_count": 0,
          "statuses_count": 0,
          "note": "",
          "url": "https://example.com/@bar",
          "uri": "https://example.com/@bar",
        },
        "url": "https://example.com/@bar/1",
        "uri": "https://example.com/@bar/1",
        "content": "",
        "emojis": [],
        "reblogs_count": 0,
        "favourites_count": 0,
        "sensitive": false,
        "spoiler_text": "",
        "visibility": "public",
        "media_attachments": [],
        "mentions": [],
        "tags": [],
      }
    }
    """
    lazy var likeA1Data = """
    {
      "id": "1",
      "type": "favourite",
      "created_at": "2019-11-23T07:29:18Z",
      "account": {
        "id": "1",
        "username": "foo",
        "acct": "foo",
        "display_name": "foo",
        "locked": false,
        "created_at": "2019-11-01T01:01:01Z",
        "followers_count": 0,
        "following_count": 0,
        "statuses_count": 0,
        "note": "",
        "url": "https://example.com/@foo",
        "uri": "https://example.com/@foo",
      },
      "status": \(statusA)
    """.data(using: .utf8)!
    lazy var likeA1 = try! decoder.decode(Notification.self, from: likeA1Data)
    lazy var likeA2Data = """
    {
      "id": "2",
      "type": "favourite",
      "created_at": "2019-11-23T07:30:00Z",
      "account": {
        "id": "2",
        "username": "baz",
        "acct": "baz",
        "display_name": "baz",
        "locked": false,
        "created_at": "2019-11-01T01:01:01Z",
        "followers_count": 0,
        "following_count": 0,
        "statuses_count": 0,
        "note": "",
        "url": "https://example.com/@baz",
        "uri": "https://example.com/@baz",
      },
      "status": \(statusA)
    """.data(using: .utf8)!
    lazy var likeA2 = try! decoder.decode(Notification.self, from: likeA2Data)
    let statusB = """
    {
        "id": "2",
        "created_at": "2019-11-23T07:28:34Z",
        "account": {
          "id": "2",
          "username": "bar",
          "acct": "bar",
          "display_name": "bar",
          "locked": false,
          "created_at": "2019-11-01T01:01:01Z",
          "followers_count": 0,
          "following_count": 0,
          "statuses_count": 0,
          "note": "",
          "url": "https://example.com/@bar",
          "uri": "https://example.com/@bar",
        },
        "url": "https://example.com/@bar/1",
        "uri": "https://example.com/@bar/1",
        "content": "",
        "emojis": [],
        "reblogs_count": 0,
        "favourites_count": 0,
        "sensitive": false,
        "spoiler_text": "",
        "visibility": "public",
        "media_attachments": [],
        "mentions": [],
        "tags": [],
      }
    }
    """
    lazy var likeBData = """
    {
      "id": "3",
      "type": "favourite",
      "created_at": "2019-11-23T07:29:18Z",
      "account": {
        "id": "1",
        "username": "foo",
        "acct": "foo",
        "display_name": "foo",
        "locked": false,
        "created_at": "2019-11-01T01:01:01Z",
        "followers_count": 0,
        "following_count": 0,
        "statuses_count": 0,
        "note": "",
        "url": "https://example.com/@foo",
        "uri": "https://example.com/@foo",
      },
      "status": \(statusB)
    """.data(using: .utf8)!
    lazy var likeB = try! decoder.decode(Notification.self, from: likeBData)
    lazy var likeB2Data = """
    {
      "id": "4",
      "type": "favourite",
      "created_at": "2019-11-23T07:29:18Z",
      "account": {
        "id": "2",
        "username": "bar",
        "acct": "bar",
        "display_name": "bar",
        "locked": false,
        "created_at": "2019-11-02T01:01:01Z",
        "followers_count": 0,
        "following_count": 0,
        "statuses_count": 0,
        "note": "",
        "url": "https://example.com/@bar",
        "uri": "https://example.com/@bar",
      },
      "status": \(statusB)
    """.data(using: .utf8)!
    lazy var likeB2 = try! decoder.decode(Notification.self, from: likeB2Data)
    lazy var mentionBData = """
    {
      "id": "5",
      "type": "mention",
      "created_at": "2019-11-23T07:29:18Z",
      "account": {
        "id": "1",
        "username": "foo",
        "acct": "foo",
        "display_name": "foo",
        "locked": false,
        "created_at": "2019-11-01T01:01:01Z",
        "followers_count": 0,
        "following_count": 0,
        "statuses_count": 0,
        "note": "",
        "url": "https://example.com/@foo",
        "uri": "https://example.com/@foo",
      },
      "status": \(statusB)
    """.data(using: .utf8)!
    lazy var mentionB = try! decoder.decode(Notification.self, from: mentionBData)


    func testGroupSimple() {
        let groups = NotificationGroup.createGroups(notifications: [likeA1, likeA2], only: [.favourite])
        XCTAssertEqual(groups, [NotificationGroup(notifications: [likeA1, likeA2])!])
    }
    
    func testGroupWithOtherGroupableInBetween() {
        let groups = NotificationGroup.createGroups(notifications: [likeA1, likeB, likeA2], only: [.favourite])
        XCTAssertEqual(groups, [
            NotificationGroup(notifications: [likeA1, likeA2])!,
            NotificationGroup(notifications: [likeB])!,
        ])
    }
    
    func testDontGroupWithUngroupableInBetween() {
        let groups = NotificationGroup.createGroups(notifications: [likeA1, mentionB, likeA2], only: [.favourite])
        XCTAssertEqual(groups, [
            NotificationGroup(notifications: [likeA1])!,
            NotificationGroup(notifications: [mentionB])!,
            NotificationGroup(notifications: [likeA2])!,
        ])
    }

    func testMergeSimpleGroups() {
        let group1 = NotificationGroup(notifications: [likeA1])!
        let group2 = NotificationGroup(notifications: [likeA2])!
        let merged = NotificationGroup.mergeGroups(first: [group1], second: [group2], only: [.favourite])
        XCTAssertEqual(merged, [
            NotificationGroup(notifications: [likeA1, likeA2])!
        ])
    }
    
    func testMergeGroupsWithOtherGroupableInBetween() {
        let group1 = NotificationGroup(notifications: [likeA1])!
        let group2 = NotificationGroup(notifications: [likeB])!
        let group3 = NotificationGroup(notifications: [likeA2])!
        let merged = NotificationGroup.mergeGroups(first: [group1, group2], second: [group3], only: [.favourite])
        XCTAssertEqual(merged, [
            NotificationGroup(notifications: [likeA1, likeA2])!,
            NotificationGroup(notifications: [likeB])!,
        ])
        
        let merged2 = NotificationGroup.mergeGroups(first: [group1], second: [group2, group3], only: [.favourite])
        XCTAssertEqual(merged2, [
            NotificationGroup(notifications: [likeA1, likeA2])!,
            NotificationGroup(notifications: [likeB])!,
        ])
        
        let group4 = NotificationGroup(notifications: [likeB2])!
        let group5 = NotificationGroup(notifications: [mentionB])!
        let merged3 = NotificationGroup.mergeGroups(first: [group1, group5, group2], second: [group4, group3], only: [.favourite])
        print(merged3.count)
        XCTAssertEqual(merged3, [
            group1,
            group5,
            NotificationGroup(notifications: [likeB, likeB2]),
            group3
        ])
    }
    
    func testDontMergeWithUngroupableInBetween() {
        let group1 = NotificationGroup(notifications: [likeA1])!
        let group2 = NotificationGroup(notifications: [mentionB])!
        let group3 = NotificationGroup(notifications: [likeA2])!
        let merged = NotificationGroup.mergeGroups(first: [group1, group2], second: [group3], only: [.favourite])
        XCTAssertEqual(merged, [
            NotificationGroup(notifications: [likeA1])!,
            NotificationGroup(notifications: [mentionB])!,
            NotificationGroup(notifications: [likeA2])!,
        ])
    }

}
