import Foundation

public enum UnreadMessageCountsItem: Equatable {
    case total(ValueBoxKey?)
    case totalInGroup(PeerGroupId)
    case peer(PeerId)
}

private enum MutableUnreadMessageCountsItemEntry {
    case total((ValueBoxKey, PreferencesEntry?)?, ChatListTotalUnreadState)
    case totalInGroup(PeerGroupId, ChatListTotalUnreadState)
    case peer(PeerId, CombinedPeerReadState?)
}

public enum UnreadMessageCountsItemEntry {
    case total(PreferencesEntry?, ChatListTotalUnreadState)
    case totalInGroup(PeerGroupId, ChatListTotalUnreadState)
    case peer(PeerId, CombinedPeerReadState?)
}

final class MutableUnreadMessageCountsView: MutablePostboxView {
    fileprivate var entries: [MutableUnreadMessageCountsItemEntry]
    
    init(postbox: Postbox, items: [UnreadMessageCountsItem]) {
        self.entries = items.map { item in
            switch item {
            case let .total(preferencesKey):
                return .total(preferencesKey.flatMap({ ($0, postbox.preferencesTable.get(key: $0)) }), postbox.messageHistoryMetadataTable.getTotalUnreadState(groupId: .root))
            case let .totalInGroup(groupId):
                return .totalInGroup(groupId, postbox.messageHistoryMetadataTable.getTotalUnreadState(groupId: groupId))
            case let .peer(peerId):
                return .peer(peerId, postbox.readStateTable.getCombinedState(peerId))
            }
        }
    }
    
    func replay(postbox: Postbox, transaction: PostboxTransaction) -> Bool {
        var updated = false
        
        var updatedPreferencesEntry: PreferencesEntry?
        if !transaction.currentPreferencesOperations.isEmpty {
            for i in 0 ..< self.entries.count {
                if case let .total(maybeKeyAndValue, _) = self.entries[i], let (key, _) = maybeKeyAndValue {
                    for operation in transaction.currentPreferencesOperations {
                        if case let .update(updateKey, value) = operation {
                            if key == updateKey {
                                updatedPreferencesEntry = value
                            }
                        }
                    }
                }
            }
        }
        
        if !transaction.currentUpdatedTotalUnreadStates.isEmpty || !transaction.alteredInitialPeerCombinedReadStates.isEmpty || updatedPreferencesEntry != nil {
            for i in 0 ..< self.entries.count {
                switch self.entries[i] {
                case let .total(keyAndEntry, state):
                    if let updatedState = transaction.currentUpdatedTotalUnreadStates[.root] {
                        if updatedState != state {
                            self.entries[i] = .total(keyAndEntry.flatMap({ ($0.0, updatedPreferencesEntry ?? $0.1) }), updatedState)
                            updated = true
                        }
                    }
                case let .totalInGroup(groupId, state):
                    if let updatedState = transaction.currentUpdatedTotalUnreadStates[groupId] {
                        if updatedState != state {
                            self.entries[i] = .totalInGroup(groupId, updatedState)
                            updated = true
                        }
                    }
                case let .peer(peerId, _):
                    if transaction.alteredInitialPeerCombinedReadStates[peerId] != nil {
                        self.entries[i] = .peer(peerId, postbox.readStateTable.getCombinedState(peerId))
                        updated = true
                    }
                }
            }
        }
        
        return updated
    }
    
    func immutableView() -> PostboxView {
        return UnreadMessageCountsView(self)
    }
}

public final class UnreadMessageCountsView: PostboxView {
    public let entries: [UnreadMessageCountsItemEntry]
    
    init(_ view: MutableUnreadMessageCountsView) {
        self.entries = view.entries.map { entry in
            switch entry {
            case let .total(keyAndValue, state):
                return .total(keyAndValue?.1, state)
            case let .totalInGroup(groupId, state):
                return .totalInGroup(groupId, state)
            case let .peer(peerId, count):
                return .peer(peerId, count)
            }
        }
    }
    
    public func total() -> (PreferencesEntry?, ChatListTotalUnreadState)? {
        for entry in self.entries {
            switch entry {
            case let .total(preferencesEntry, state):
                return (preferencesEntry, state)
            default:
                break
            }
        }
        return nil
    }
    
    public func count(for item: UnreadMessageCountsItem) -> Int32? {
        for entry in self.entries {
            switch entry {
            case .total, .totalInGroup:
                break
            case let .peer(peerId, state):
                if case .peer(peerId) = item {
                    return state?.count ?? 0
                }
            }
        }
        return nil
    }
}

final class MutableCombinedReadStateView: MutablePostboxView {
    private let peerId: PeerId
    fileprivate var state: CombinedPeerReadState?
    
    init(postbox: Postbox, peerId: PeerId) {
        self.peerId = peerId
        self.state = postbox.readStateTable.getCombinedState(peerId)
    }
    
    func replay(postbox: Postbox, transaction: PostboxTransaction) -> Bool {
        var updated = false
        
        if transaction.alteredInitialPeerCombinedReadStates[self.peerId] != nil {
            let state = postbox.readStateTable.getCombinedState(peerId)
            if state != self.state {
                self.state = state
                updated = true
            }
        }
        
        return updated
    }
    
    func immutableView() -> PostboxView {
        return CombinedReadStateView(self)
    }
}

public final class CombinedReadStateView: PostboxView {
    public let state: CombinedPeerReadState?
    
    init(_ view: MutableCombinedReadStateView) {
        self.state = view.state
    }
}