import Foundation
import Postbox
import SwiftSignalKit
import TelegramApi
import MtProtoKit


public enum ChannelMembersCategoryFilter {
    case all
    case search(String)
}

public enum ChannelMembersCategory {
    case recent(ChannelMembersCategoryFilter)
    case admins
    case contacts(ChannelMembersCategoryFilter)
    case bots(ChannelMembersCategoryFilter)
    case restricted(ChannelMembersCategoryFilter)
    case banned(ChannelMembersCategoryFilter)
    case mentions(threadId: MessageId?, filter: ChannelMembersCategoryFilter)
}

func _internal_channelMembers(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, category: ChannelMembersCategory = .recent(.all), offset: Int32 = 0, limit: Int32 = 64, hash: Int64 = 0) -> Signal<[RenderedChannelParticipant]?, NoError> {
    return postbox.transaction { transaction -> Signal<[RenderedChannelParticipant]?, NoError> in
        if let peer = transaction.getPeer(peerId) as? TelegramChannel, let inputChannel = apiInputChannel(peer) {
            if case .broadcast = peer.info {
                if let _ = peer.adminRights {
                } else {
                    return .single(nil)
                }
            }
            
            let apiFilter: Api.ChannelParticipantsFilter
            switch category {
                case let .recent(filter):
                    switch filter {
                        case .all:
                            apiFilter = .channelParticipantsRecent
                        case let .search(query):
                            apiFilter = .channelParticipantsSearch(q: query)
                    }
                case let .mentions(threadId, filter):
                    switch filter {
                        case .all:
                            var flags: Int32 = 0
                            if threadId != nil {
                                flags |= 1 << 1
                            }
                            apiFilter = .channelParticipantsMentions(flags: flags, q: nil, topMsgId: threadId?.id)
                        case let .search(query):
                            var flags: Int32 = 0
                            if threadId != nil {
                                flags |= 1 << 1
                            }
                            if !query.isEmpty {
                                flags |= 1 << 0
                            }
                            apiFilter = .channelParticipantsMentions(flags: flags, q: query.isEmpty ? nil : query, topMsgId: threadId?.id)
                    }
                case .admins:
                    apiFilter = .channelParticipantsAdmins
                case let .contacts(filter):
                    switch filter {
                        case .all:
                            apiFilter = .channelParticipantsContacts(q: "")
                        case let .search(query):
                            apiFilter = .channelParticipantsContacts(q: query)
                    }
                case .bots:
                    apiFilter = .channelParticipantsBots
                case let .restricted(filter):
                    switch filter {
                        case .all:
                            apiFilter = .channelParticipantsBanned(q: "")
                        case let .search(query):
                            apiFilter = .channelParticipantsBanned(q: query)
                    }
                case let .banned(filter):
                    switch filter {
                        case .all:
                            apiFilter = .channelParticipantsKicked(q: "")
                        case let .search(query):
                            apiFilter = .channelParticipantsKicked(q: query)
                    }
            }
            return network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: apiFilter, offset: offset, limit: limit, hash: hash))
                |> retryRequest
                |> mapToSignal { result -> Signal<[RenderedChannelParticipant]?, NoError> in
                    return postbox.transaction { transaction -> [RenderedChannelParticipant]? in
                        var items: [RenderedChannelParticipant] = []
                        switch result {
                            case let .channelParticipants(_, participants, chats, users):
                                let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users)
                                updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers)
                                var peers: [PeerId: Peer] = [:]
                                for id in parsedPeers.allIds {
                                    if let peer = transaction.getPeer(id) {
                                        peers[peer.id] = peer
                                    }
                                }
                                
                                for participant in CachedChannelParticipants(apiParticipants: participants).participants {
                                    if let peer = parsedPeers.get(participant.peerId) {
                                        var renderedPresences: [PeerId: PeerPresence] = [:]
                                        if let presence = transaction.getPeerPresence(peerId: participant.peerId) {
                                            renderedPresences[participant.peerId] = presence
                                        }
                                        items.append(RenderedChannelParticipant(participant: participant, peer: peer, peers: peers, presences: renderedPresences))
                                    }
                                }
                            case .channelParticipantsNotModified:
                                return nil
                        }
                        return items
                    }
            }
        } else {
            return .single([])
        }
    } |> switchToLatest
}