import Foundation
import UIKit
import Display
import ComponentFlow
import TelegramPresentationData
import TelegramCore
import Postbox
import AnimationCache
import MultiAnimationRenderer
import AccountContext
import AsyncDisplayKit
import ComponentDisplayAdapters
import PagerComponent
import SwiftSignalKit

public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode {
    private struct Params: Equatable {
        var size: CGSize
        var leftInset: CGFloat
        var rightInset: CGFloat
        var bottomInset: CGFloat
        var inputHeight: CGFloat
        var deviceMetrics: DeviceMetrics
    }
    
    private struct EmojiSearchResult {
        var groups: [EmojiPagerContentComponent.ItemGroup]
        var id: AnyHashable
        var version: Int
        var isPreset: Bool
    }
    
    private struct EmojiSearchState {
        var result: EmojiSearchResult?
        var isSearching: Bool
        
        init(result: EmojiSearchResult?, isSearching: Bool) {
            self.result = result
            self.isSearching = isSearching
        }
    }
    
    private let context: AccountContext
    private var initialFocusId: ItemCollectionId?
    private let hasPremiumForUse: Bool
    private let hasPremiumForInstallation: Bool
    private let parentInputInteraction: EmojiPagerContentComponent.InputInteraction
    private var presentationData: PresentationData
    
    private let keyboardView = ComponentView<Empty>()
    private let panelHostView: PagerExternalTopPanelContainer
    private let inputInteractionHolder: EmojiPagerContentComponent.InputInteractionHolder
    
    private var params: Params?
    
    private var itemGroups: [EmojiPagerContentComponent.ItemGroup] = []
    
    public var onCancel: (() -> Void)?
    
    private let emojiSearchDisposable = MetaDisposable()
    private let emojiSearchState = Promise<EmojiSearchState>(EmojiSearchState(result: nil, isSearching: false))
    private var emojiSearchStateValue = EmojiSearchState(result: nil, isSearching: false) {
        didSet {
            self.emojiSearchState.set(.single(self.emojiSearchStateValue))
        }
    }
    private var immediateEmojiSearchState: EmojiSearchState = EmojiSearchState(result: nil, isSearching: false)
    
    private var dataDisposable: Disposable?

    public init(
        context: AccountContext,
        items: [FeaturedStickerPackItem],
        initialFocusId: ItemCollectionId?,
        hasPremiumForUse: Bool,
        hasPremiumForInstallation: Bool,
        parentInputInteraction: EmojiPagerContentComponent.InputInteraction
    ) {
        self.context = context
        self.initialFocusId = initialFocusId
        self.hasPremiumForUse = hasPremiumForUse
        self.hasPremiumForInstallation = hasPremiumForInstallation
        self.parentInputInteraction = parentInputInteraction
        
        self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
        
        self.panelHostView = PagerExternalTopPanelContainer()
        self.inputInteractionHolder = EmojiPagerContentComponent.InputInteractionHolder()
        
        super.init()
        
        for groupItem in items {
            var groupItems: [EmojiPagerContentComponent.Item] = []
            for item in groupItem.topItems {
                var tintMode: EmojiPagerContentComponent.Item.TintMode = .none
                if item.file.isCustomTemplateEmoji {
                    tintMode = .primary
                }
                
                let animationData = EntityKeyboardAnimationData(file: item.file)
                let resultItem = EmojiPagerContentComponent.Item(
                    animationData: animationData,
                    content: .animation(animationData),
                    itemFile: item.file,
                    subgroupId: nil,
                    icon: .none,
                    tintMode: tintMode
                )
                
                groupItems.append(resultItem)
            }
            
            self.itemGroups.append(EmojiPagerContentComponent.ItemGroup(
                supergroupId: AnyHashable(groupItem.info.id),
                groupId: AnyHashable(groupItem.info.id),
                title: groupItem.info.title,
                subtitle: nil,
                actionButtonTitle: self.presentationData.strings.EmojiInput_AddPack(groupItem.info.title).string,
                isFeatured: true,
                isPremiumLocked: !self.hasPremiumForInstallation,
                isEmbedded: false,
                hasClear: false,
                collapsedLineCount: 3,
                displayPremiumBadges: false,
                headerItem: nil,
                fillWithLoadingPlaceholders: false,
                items: groupItems
            ))
        }
        
        self.inputInteractionHolder.inputInteraction = EmojiPagerContentComponent.InputInteraction(
            performItemAction: { [weak self] groupId, item, sourceView, sourceRect, sourceLayer, isPreview in
                guard let self else {
                    return
                }
                self.parentInputInteraction.performItemAction(groupId, item, sourceView, sourceRect, sourceLayer, isPreview)
                if self.hasPremiumForUse {
                    self.onCancel?()
                }
            },
            deleteBackwards: {
            },
            openStickerSettings: {
            },
            openFeatured: {
            },
            openSearch: {
            },
            addGroupAction: { [weak self] groupId, isPremiumLocked, _ in
                guard let self else {
                    return
                }
                self.parentInputInteraction.addGroupAction(groupId, isPremiumLocked, false)
                
                if !isPremiumLocked {
                    if self.itemGroups.count == 1 {
                        self.onCancel?()
                    } else {
                        self.itemGroups.removeAll(where: { $0.groupId == groupId })
                        self.update(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(EmojiPagerContentComponent.ContentAnimation(type: .groupRemoved(id: groupId))))
                    }
                }
            },
            clearGroup: { _ in
            },
            pushController: { _ in
            },
            presentController: { _ in
            },
            presentGlobalOverlayController: { _ in
            },
            navigationController: {
                return nil
            },
            requestUpdate: { _ in
            },
            updateSearchQuery: { [weak self] query in
                guard let self else {
                    return
                }
                
                switch query {
                case .none:
                    self.emojiSearchDisposable.set(nil)
                    self.emojiSearchState.set(.single(EmojiSearchState(result: nil, isSearching: false)))
                case let .text(rawQuery, languageCode):
                    let query = rawQuery.trimmingCharacters(in: .whitespacesAndNewlines)
                    
                    if query.isEmpty {
                        self.emojiSearchDisposable.set(nil)
                        self.emojiSearchState.set(.single(EmojiSearchState(result: nil, isSearching: false)))
                    } else {
                        let context = self.context
                        
                        var signal = context.engine.stickers.searchEmojiKeywords(inputLanguageCode: languageCode, query: query, completeMatch: false)
                        if !languageCode.lowercased().hasPrefix("en") {
                            signal = signal
                            |> mapToSignal { keywords in
                                return .single(keywords)
                                |> then(
                                    context.engine.stickers.searchEmojiKeywords(inputLanguageCode: "en-US", query: query, completeMatch: query.count < 3)
                                    |> map { englishKeywords in
                                        return keywords + englishKeywords
                                    }
                                )
                            }
                        }
                    
                        let hasPremium = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
                        |> map { peer -> Bool in
                            guard case let .user(user) = peer else {
                                return false
                            }
                            return user.isPremium
                        }
                        |> distinctUntilChanged
                        
                        let resultSignal = signal
                        |> mapToSignal { keywords -> Signal<[EmojiPagerContentComponent.ItemGroup], NoError> in
                            return combineLatest(
                                context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000),
                                context.engine.stickers.availableReactions(),
                                hasPremium
                            )
                            |> take(1)
                            |> map { view, availableReactions, hasPremium -> [EmojiPagerContentComponent.ItemGroup] in
                                var result: [(String, TelegramMediaFile?, String)] = []
                                
                                var allEmoticons: [String: String] = [:]
                                for keyword in keywords {
                                    for emoticon in keyword.emoticons {
                                        allEmoticons[emoticon] = keyword.keyword
                                    }
                                }
                                
                                for entry in view.entries {
                                    guard let item = entry.item as? StickerPackItem else {
                                        continue
                                    }
                                    for attribute in item.file.attributes {
                                        switch attribute {
                                        case let .CustomEmoji(_, _, alt, _):
                                            if !item.file.isPremiumEmoji || hasPremium {
                                                if !alt.isEmpty, let keyword = allEmoticons[alt] {
                                                    result.append((alt, item.file, keyword))
                                                } else if alt == query {
                                                    result.append((alt, item.file, alt))
                                                }
                                            }
                                        default:
                                            break
                                        }
                                    }
                                }
                                
                                var items: [EmojiPagerContentComponent.Item] = []
                                
                                var existingIds = Set<MediaId>()
                                for item in result {
                                    if let itemFile = item.1 {
                                        if existingIds.contains(itemFile.fileId) {
                                            continue
                                        }
                                        existingIds.insert(itemFile.fileId)
                                        let animationData = EntityKeyboardAnimationData(file: itemFile)
                                        let item = EmojiPagerContentComponent.Item(
                                            animationData: animationData,
                                            content: .animation(animationData),
                                            itemFile: itemFile, subgroupId: nil,
                                            icon: .none,
                                            tintMode: animationData.isTemplate ? .primary : .none
                                        )
                                        items.append(item)
                                    }
                                }
                                
                                return [EmojiPagerContentComponent.ItemGroup(
                                    supergroupId: "search",
                                    groupId: "search",
                                    title: nil,
                                    subtitle: nil,
                                    actionButtonTitle: nil,
                                    isFeatured: false,
                                    isPremiumLocked: false,
                                    isEmbedded: false,
                                    hasClear: false,
                                    collapsedLineCount: nil,
                                    displayPremiumBadges: false,
                                    headerItem: nil,
                                    fillWithLoadingPlaceholders: false,
                                    items: items
                                )]
                            }
                        }
                        
                        var version = 0
                        self.emojiSearchStateValue.isSearching = true
                        self.emojiSearchDisposable.set((resultSignal
                        |> delay(0.15, queue: .mainQueue())
                        |> deliverOnMainQueue).start(next: { [weak self] result in
                            guard let self else {
                                return
                            }
                            
                            self.emojiSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result, id: AnyHashable(query), version: version, isPreset: false), isSearching: false)
                            version += 1
                        }))
                    }
                case let .category(value):
                    let resultSignal = self.context.engine.stickers.searchEmoji(emojiString: value)
                    |> mapToSignal { files, isFinalResult -> Signal<(items: [EmojiPagerContentComponent.ItemGroup], isFinalResult: Bool), NoError> in
                        var items: [EmojiPagerContentComponent.Item] = []
                        
                        var existingIds = Set<MediaId>()
                        for itemFile in files {
                            if existingIds.contains(itemFile.fileId) {
                                continue
                            }
                            existingIds.insert(itemFile.fileId)
                            let animationData = EntityKeyboardAnimationData(file: itemFile)
                            let item = EmojiPagerContentComponent.Item(
                                animationData: animationData,
                                content: .animation(animationData),
                                itemFile: itemFile, subgroupId: nil,
                                icon: .none,
                                tintMode: animationData.isTemplate ? .primary : .none
                            )
                            items.append(item)
                        }
                        
                        return .single(([EmojiPagerContentComponent.ItemGroup(
                            supergroupId: "search",
                            groupId: "search",
                            title: nil,
                            subtitle: nil,
                            actionButtonTitle: nil,
                            isFeatured: false,
                            isPremiumLocked: false,
                            isEmbedded: false,
                            hasClear: false,
                            collapsedLineCount: nil,
                            displayPremiumBadges: false,
                            headerItem: nil,
                            fillWithLoadingPlaceholders: false,
                            items: items
                        )], isFinalResult))
                    }
                    
                    let _ = resultSignal
                        
                    var version = 0
                    self.emojiSearchDisposable.set((resultSignal
                    |> deliverOnMainQueue).start(next: { [weak self] result in
                        guard let self else {
                            return
                        }
                        
                        guard let group = result.items.first else {
                            return
                        }
                        if group.items.isEmpty && !result.isFinalResult {
                            //self.emojiSearchStateValue.isSearching = true
                            self.emojiSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: [
                                EmojiPagerContentComponent.ItemGroup(
                                    supergroupId: "search",
                                    groupId: "search",
                                    title: nil,
                                    subtitle: nil,
                                    actionButtonTitle: nil,
                                    isFeatured: false,
                                    isPremiumLocked: false,
                                    isEmbedded: false,
                                    hasClear: false,
                                    collapsedLineCount: nil,
                                    displayPremiumBadges: false,
                                    headerItem: nil,
                                    fillWithLoadingPlaceholders: true,
                                    items: []
                                )
                            ], id: AnyHashable(value), version: version, isPreset: true), isSearching: false)
                            return
                        }
                        
                        self.emojiSearchStateValue = EmojiSearchState(result: EmojiSearchResult(groups: result.items, id: AnyHashable(value), version: version, isPreset: false), isSearching: false)
                        version += 1
                    }))
                }
            },
            updateScrollingToItemGroup: {
            },
            externalCancel: { [weak self] in
                guard let self else {
                    return
                }
                self.onCancel?()
            },
            onScroll: {},
            chatPeerId: nil,
            peekBehavior: nil,
            customLayout: nil,
            externalBackground: nil,
            externalExpansionView: nil,
            useOpaqueTheme: true,
            hideBackground: false
        )
        
        self.dataDisposable = (
            self.emojiSearchState.get()
            |> deliverOnMainQueue
        ).start(next: { [weak self] emojiSearchState in
            guard let self else {
                return
            }
            self.immediateEmojiSearchState = emojiSearchState
            self.update(transition: .immediate)
        })
    }
    
    deinit {
        self.emojiSearchDisposable.dispose()
        self.dataDisposable?.dispose()
    }
    
    private func update(transition: Transition) {
        if let params = self.params {
            self.update(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, bottomInset: params.bottomInset, inputHeight: params.inputHeight, deviceMetrics: params.deviceMetrics, transition: transition)
        }
    }
    
    public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, inputHeight: CGFloat, deviceMetrics: DeviceMetrics, transition: ContainedViewLayoutTransition) {
        self.update(size: size, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, inputHeight: inputHeight, deviceMetrics: deviceMetrics, transition: Transition(transition))
    }
     
    private func update(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, inputHeight: CGFloat, deviceMetrics: DeviceMetrics, transition: Transition) {
        self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
        
        let params = Params(size: size, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, inputHeight: inputHeight, deviceMetrics: deviceMetrics)
        self.params = params
        
        var emojiContent = EmojiPagerContentComponent(
            id: "emoji",
            context: self.context,
            avatarPeer: nil,
            animationCache: self.context.animationCache,
            animationRenderer: self.context.animationRenderer,
            inputInteractionHolder: self.inputInteractionHolder,
            panelItemGroups: [],
            contentItemGroups: self.itemGroups,
            itemLayoutType: .compact,
            itemContentUniqueId: EmojiPagerContentComponent.ContentId(id: "main", version: 0),
            searchState: .empty(hasResults: false),
            warpContentsOnEdges: false,
            displaySearchWithPlaceholder: self.presentationData.strings.EmojiSearch_SearchEmojiPlaceholder,
            searchCategories: nil,
            searchInitiallyHidden: false,
            searchAlwaysActive: true,
            searchIsPlaceholderOnly: false,
            emptySearchResults: nil,
            enableLongPress: false,
            selectedItems: Set()
        )
        
        if let emojiSearchResult = self.immediateEmojiSearchState.result {
            var emptySearchResults: EmojiPagerContentComponent.EmptySearchResults?
            if !emojiSearchResult.groups.contains(where: { !$0.items.isEmpty || $0.fillWithLoadingPlaceholders }) {
                emptySearchResults = EmojiPagerContentComponent.EmptySearchResults(
                    text: self.presentationData.strings.EmojiSearch_SearchEmojiEmptyResult,
                    iconFile: nil
                )
            }
            emojiContent = emojiContent.withUpdatedItemGroups(panelItemGroups: emojiContent.panelItemGroups, contentItemGroups: emojiSearchResult.groups, itemContentUniqueId: EmojiPagerContentComponent.ContentId(id: emojiSearchResult.id, version: emojiSearchResult.version), emptySearchResults: emptySearchResults, searchState: self.immediateEmojiSearchState.isSearching ? .searching : .empty(hasResults: true))
        }
        
        let _ = self.keyboardView.update(
            transition: transition.withUserData(EmojiPagerContentComponent.SynchronousLoadBehavior(isDisabled: true)),
            component: AnyComponent(EntityKeyboardComponent(
                theme: self.presentationData.theme,
                strings: self.presentationData.strings,
                isContentInFocus: true,
                containerInsets: UIEdgeInsets(top: 0.0, left: leftInset, bottom: bottomInset, right: rightInset),
                topPanelInsets: UIEdgeInsets(top: 0.0, left: 4.0, bottom: 0.0, right: 4.0),
                emojiContent: emojiContent,
                stickerContent: nil,
                maskContent: nil,
                gifContent: nil,
                hasRecentGifs: false,
                availableGifSearchEmojies: [],
                defaultToEmojiTab: true,
                externalTopPanelContainer: self.panelHostView,
                externalBottomPanelContainer: nil,
                displayTopPanelBackground: .blur,
                topPanelExtensionUpdated: { _, _ in },
                hideInputUpdated: { _, _, _ in },
                hideTopPanelUpdated: { _, _ in
                },
                switchToTextInput: {},
                switchToGifSubject: { _ in },
                reorderItems: { _, _ in },
                makeSearchContainerNode: { _ in return nil },
                contentIdUpdated: { _ in },
                deviceMetrics: deviceMetrics,
                hiddenInputHeight: 0.0,
                inputHeight: 0.0,
                displayBottomPanel: false,
                isExpanded: false,
                clipContentToTopPanel: false,
                hidePanels: true
            )),
            environment: {},
            containerSize: size
        )
        if let keyboardComponentView = self.keyboardView.view as? EntityKeyboardComponent.View {
            if keyboardComponentView.superview == nil {
                self.view.addSubview(keyboardComponentView)
            }
            transition.setFrame(view: keyboardComponentView, frame: CGRect(origin: CGPoint(), size: size))
            
            if let initialFocusId = self.initialFocusId {
                self.initialFocusId = nil
                
                keyboardComponentView.scrollToItemGroup(contentId: "emoji", groupId: AnyHashable(initialFocusId), subgroupId: nil, animated: false)
            }
        }
    }
}