import Foundation
import UIKit
import Photos
import Display
import AsyncDisplayKit
import SwiftSignalKit
import Postbox
import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
import PresentationDataUtils
import AccountContext
import ComponentFlow
import ViewControllerComponent
import MultilineTextComponent
import BalancedTextComponent
import BackButtonComponent
import ListSectionComponent
import ListActionItemComponent
import ListTextFieldItemComponent
import BundleIconComponent
import LottieComponent
import Markdown
import PeerListItemComponent
import AvatarNode

private let checkIcon: UIImage = {
    return generateImage(CGSize(width: 12.0, height: 10.0), rotatedContext: { size, context in
        context.clear(CGRect(origin: CGPoint(), size: size))
        context.setStrokeColor(UIColor.white.cgColor)
        context.setLineWidth(1.98)
        context.setLineCap(.round)
        context.setLineJoin(.round)
        context.translateBy(x: 1.0, y: 1.0)
        
        let _ = try? drawSvgPath(context, path: "M0.215053763,4.36080467 L3.31621263,7.70466293 L3.31621263,7.70466293 C3.35339229,7.74475231 3.41603123,7.74711109 3.45612061,7.70993143 C3.45920681,7.70706923 3.46210733,7.70401312 3.46480451,7.70078171 L9.89247312,0 S ")
    })!.withRenderingMode(.alwaysTemplate)
}()

final class ChatbotSetupScreenComponent: Component {
    typealias EnvironmentType = ViewControllerComponentContainer.Environment
    
    let context: AccountContext
    let initialData: ChatbotSetupScreen.InitialData

    init(
        context: AccountContext,
        initialData: ChatbotSetupScreen.InitialData
    ) {
        self.context = context
        self.initialData = initialData
    }

    static func ==(lhs: ChatbotSetupScreenComponent, rhs: ChatbotSetupScreenComponent) -> Bool {
        if lhs.context !== rhs.context {
            return false
        }

        return true
    }
    
    private final class ScrollView: UIScrollView {
        override func touchesShouldCancel(in view: UIView) -> Bool {
            return true
        }
    }
    
    private struct BotResolutionState: Equatable {
        enum State: Equatable {
            case searching
            case notFound
            case found(peer: EnginePeer, isInstalled: Bool)
        }
        
        var query: String
        var state: State
        
        init(query: String, state: State) {
            self.query = query
            self.state = state
        }
    }
    
    struct AdditionalPeerList {
        enum Category: Int {
            case newChats = 0
            case existingChats = 1
            case contacts = 2
            case nonContacts = 3
        }
        
        struct Peer {
            var peer: EnginePeer
            var isContact: Bool
            
            init(peer: EnginePeer, isContact: Bool) {
                self.peer = peer
                self.isContact = isContact
            }
        }
        
        var categories: Set<Category>
        var peers: [Peer]
        var excludePeers: [Peer]
        
        init(categories: Set<Category>, peers: [Peer], excludePeers: [Peer]) {
            self.categories = categories
            self.peers = peers
            self.excludePeers = excludePeers
        }
    }
    
    final class View: UIView, UIScrollViewDelegate {
        private let topOverscrollLayer = SimpleLayer()
        private let scrollView: ScrollView
        
        private let navigationTitle = ComponentView<Empty>()
        private let icon = ComponentView<Empty>()
        private let subtitle = ComponentView<Empty>()
        private let nameSection = ComponentView<Empty>()
        private let accessSection = ComponentView<Empty>()
        private let excludedSection = ComponentView<Empty>()
        private let excludedUsersSection = ComponentView<Empty>()
        private let permissionsSection = ComponentView<Empty>()
        
        private var isUpdating: Bool = false
        
        private var component: ChatbotSetupScreenComponent?
        private(set) weak var state: EmptyComponentState?
        private var environment: EnvironmentType?
        
        private var chevronImage: UIImage?
        private let textFieldTag = NSObject()
        
        private var botResolutionState: BotResolutionState?
        private var botResolutionDisposable: Disposable?
        private var resetQueryText: String?
        
        private var hasAccessToAllChatsByDefault: Bool = true
        private var additionalPeerList = AdditionalPeerList(
            categories: Set(),
            peers: [],
            excludePeers: []
        )
        
        private var replyToMessages: Bool = true
        
        override init(frame: CGRect) {
            self.scrollView = ScrollView()
            self.scrollView.showsVerticalScrollIndicator = true
            self.scrollView.showsHorizontalScrollIndicator = false
            self.scrollView.scrollsToTop = false
            self.scrollView.delaysContentTouches = false
            self.scrollView.canCancelContentTouches = true
            self.scrollView.contentInsetAdjustmentBehavior = .never
            if #available(iOS 13.0, *) {
                self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false
            }
            self.scrollView.alwaysBounceVertical = true
            
            super.init(frame: frame)
            
            self.scrollView.delegate = self
            self.addSubview(self.scrollView)
            
            self.scrollView.layer.addSublayer(self.topOverscrollLayer)
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        deinit {
        }

        func scrollToTop() {
            self.scrollView.setContentOffset(CGPoint(), animated: true)
        }
        
        func attemptNavigation(complete: @escaping () -> Void) -> Bool {
            guard let component = self.component else {
                return true
            }
            
            var mappedCategories: TelegramBusinessRecipients.Categories = []
            if self.additionalPeerList.categories.contains(.existingChats) {
                mappedCategories.insert(.existingChats)
            }
            if self.additionalPeerList.categories.contains(.newChats) {
                mappedCategories.insert(.newChats)
            }
            if self.additionalPeerList.categories.contains(.contacts) {
                mappedCategories.insert(.contacts)
            }
            if self.additionalPeerList.categories.contains(.nonContacts) {
                mappedCategories.insert(.nonContacts)
            }
            let recipients = TelegramBusinessRecipients(
                categories: mappedCategories,
                additionalPeers: Set(self.additionalPeerList.peers.map(\.peer.id)),
                excludePeers: Set(self.additionalPeerList.excludePeers.map(\.peer.id)),
                exclude: self.hasAccessToAllChatsByDefault
            )
            
            if let botResolutionState = self.botResolutionState, case let .found(peer, isInstalled) = botResolutionState.state, isInstalled {
                let _ = component.context.engine.accountData.setAccountConnectedBot(bot: TelegramAccountConnectedBot(
                    id: peer.id,
                    recipients: recipients,
                    canReply: self.replyToMessages
                )).startStandalone()
            } else {
                let _ = component.context.engine.accountData.setAccountConnectedBot(bot: nil).startStandalone()
            }
            
            return true
        }
        
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            self.updateScrolling(transition: .immediate)
        }
        
        var scrolledUp = true
        private func updateScrolling(transition: Transition) {
            let navigationRevealOffsetY: CGFloat = 0.0
            
            let navigationAlphaDistance: CGFloat = 16.0
            let navigationAlpha: CGFloat = max(0.0, min(1.0, (self.scrollView.contentOffset.y - navigationRevealOffsetY) / navigationAlphaDistance))
            if let controller = self.environment?.controller(), let navigationBar = controller.navigationBar {
                transition.setAlpha(layer: navigationBar.backgroundNode.layer, alpha: navigationAlpha)
                transition.setAlpha(layer: navigationBar.stripeNode.layer, alpha: navigationAlpha)
            }
            
            var scrolledUp = false
            if navigationAlpha < 0.5 {
                scrolledUp = true
            } else if navigationAlpha > 0.5 {
                scrolledUp = false
            }
            
            if self.scrolledUp != scrolledUp {
                self.scrolledUp = scrolledUp
                if !self.isUpdating {
                    self.state?.updated()
                }
            }
            
            if let navigationTitleView = self.navigationTitle.view {
                transition.setAlpha(view: navigationTitleView, alpha: 1.0)
            }
        }
        
        private func updateBotQuery(query: String) {
            guard let component = self.component else {
                return
            }
            
            if !query.isEmpty {
                if self.botResolutionState?.query != query {
                    let previousState = self.botResolutionState?.state
                    let updatedState: BotResolutionState.State
                    if let current = self.botResolutionState?.state, case .found = current {
                        updatedState = current
                    } else {
                        updatedState = .searching
                    }
                    self.botResolutionState = BotResolutionState(
                        query: query,
                        state: updatedState
                    )
                    self.botResolutionDisposable?.dispose()
                    
                    if previousState != self.botResolutionState?.state {
                        self.state?.updated(transition: .spring(duration: 0.35))
                    }
                    
                    var cleanQuery = query
                    if let url = URL(string: cleanQuery), url.host == "t.me" {
                        if url.pathComponents.count > 1 {
                            cleanQuery = url.pathComponents[1]
                        }
                    } else if let url = URL(string: "https://\(cleanQuery)"), url.host == "t.me" {
                        if url.pathComponents.count > 1 {
                            cleanQuery = url.pathComponents[1]
                        }
                    }
                    
                    self.botResolutionDisposable = (component.context.engine.peers.resolvePeerByName(name: cleanQuery)
                    |> delay(0.4, queue: .mainQueue())
                    |> deliverOnMainQueue).start(next: { [weak self] result in
                        guard let self else {
                            return
                        }
                        switch result {
                        case .progress:
                            break
                        case let .result(peer):
                            let previousState = self.botResolutionState?.state
                            if let peer, case let .user(user) = peer, user.botInfo != nil {
                                self.botResolutionState?.state = .found(peer: peer, isInstalled: false)
                            } else {
                                self.botResolutionState?.state = .notFound
                            }
                            if previousState != self.botResolutionState?.state {
                                self.state?.updated(transition: .spring(duration: 0.35))
                            }
                        }
                    })
                }
            } else {
                if let botResolutionDisposable = self.botResolutionDisposable {
                    self.botResolutionDisposable = nil
                    botResolutionDisposable.dispose()
                }
                if self.botResolutionState != nil {
                    self.botResolutionState = nil
                    self.state?.updated(transition: .spring(duration: 0.35))
                }
            }
        }
        
        private func openAdditionalPeerListSetup(isExclude: Bool) {
            guard let component = self.component, let environment = self.environment else {
                return
            }
            
            var mappedPeerList = BusinessRecipientListScreenComponent.PeerList(categories: Set(), peers: [])
            if !isExclude {
                for category in self.additionalPeerList.categories {
                    switch category {
                    case .existingChats:
                        mappedPeerList.categories.insert(.existingChats)
                    case .newChats:
                        mappedPeerList.categories.insert(.newChats)
                    case .contacts:
                        mappedPeerList.categories.insert(.contacts)
                    case .nonContacts:
                        mappedPeerList.categories.insert(.nonContacts)
                    }
                }
            }
            if isExclude {
                for peer in self.additionalPeerList.excludePeers {
                    mappedPeerList.peers.append(BusinessRecipientListScreenComponent.PeerList.Peer(
                        peer: peer.peer,
                        isContact: peer.isContact
                    ))
                }
            } else {
                for peer in self.additionalPeerList.peers {
                    mappedPeerList.peers.append(BusinessRecipientListScreenComponent.PeerList.Peer(
                        peer: peer.peer,
                        isContact: peer.isContact
                    ))
                }
            }
            
            let mode: BusinessRecipientListScreen.Mode
            if isExclude {
                mode = .excludeUsers
            } else {
                if self.hasAccessToAllChatsByDefault {
                    mode = .excludeExceptions
                } else {
                    mode = .includeExceptions
                }
            }
            
            if mappedPeerList.categories.isEmpty && mappedPeerList.peers.isEmpty {
                let controller = BusinessRecipientListScreenComponent.View.makePeerListSetupScreen(
                    context: component.context,
                    mode: mode,
                    initialPeerList: mappedPeerList,
                    completion: { [weak self] peerList in
                        guard let self, let component = self.component, let environment = self.environment else {
                            return
                        }
                        
                        environment.controller()?.push(BusinessRecipientListScreen(
                            context: component.context,
                            peerList: peerList,
                            mode: mode,
                            update: { [weak self] updatedPeerList in
                                guard let self else {
                                    return
                                }
                                
                                switch mode {
                                case .excludeExceptions, .includeExceptions:
                                    self.additionalPeerList.peers.removeAll()
                                    for peer in updatedPeerList.peers {
                                        self.additionalPeerList.peers.append(AdditionalPeerList.Peer(
                                            peer: peer.peer,
                                            isContact: peer.isContact
                                        ))
                                        
                                        self.additionalPeerList.excludePeers.removeAll(where: { $0.peer.id == peer.peer.id })
                                    }
                                    self.additionalPeerList.categories.removeAll()
                                    for category in updatedPeerList.categories {
                                        switch category {
                                        case .existingChats:
                                            self.additionalPeerList.categories.insert(.existingChats)
                                        case .newChats:
                                            self.additionalPeerList.categories.insert(.newChats)
                                        case .contacts:
                                            self.additionalPeerList.categories.insert(.contacts)
                                        case .nonContacts:
                                            self.additionalPeerList.categories.insert(.nonContacts)
                                        }
                                    }
                                case .excludeUsers:
                                    for peer in updatedPeerList.peers {
                                        self.additionalPeerList.excludePeers.append(AdditionalPeerList.Peer(
                                            peer: peer.peer,
                                            isContact: peer.isContact
                                        ))
                                        
                                        self.additionalPeerList.peers.removeAll(where: { $0.peer.id == peer.peer.id })
                                    }
                                }
                                
                                self.state?.updated(transition: .immediate)
                            }
                        ))
                    }
                )
                environment.controller()?.push(controller)
            } else {
                environment.controller()?.push(BusinessRecipientListScreen(
                    context: component.context,
                    peerList: mappedPeerList,
                    mode: mode,
                    update: { [weak self] updatedPeerList in
                        guard let self else {
                            return
                        }
                        
                        switch mode {
                        case .excludeExceptions, .includeExceptions:
                            self.additionalPeerList.peers.removeAll()
                            for peer in updatedPeerList.peers {
                                self.additionalPeerList.peers.append(AdditionalPeerList.Peer(
                                    peer: peer.peer,
                                    isContact: peer.isContact
                                ))
                                
                                self.additionalPeerList.excludePeers.removeAll(where: { $0.peer.id == peer.peer.id })
                            }
                            self.additionalPeerList.categories.removeAll()
                            for category in updatedPeerList.categories {
                                switch category {
                                case .existingChats:
                                    self.additionalPeerList.categories.insert(.existingChats)
                                case .newChats:
                                    self.additionalPeerList.categories.insert(.newChats)
                                case .contacts:
                                    self.additionalPeerList.categories.insert(.contacts)
                                case .nonContacts:
                                    self.additionalPeerList.categories.insert(.nonContacts)
                                }
                            }
                        case .excludeUsers:
                            for peer in updatedPeerList.peers {
                                self.additionalPeerList.excludePeers.append(AdditionalPeerList.Peer(
                                    peer: peer.peer,
                                    isContact: peer.isContact
                                ))
                                
                                self.additionalPeerList.peers.removeAll(where: { $0.peer.id == peer.peer.id })
                            }
                        }
                        
                        self.state?.updated(transition: .immediate)
                    }
                ))
            }
        }
        
        func update(component: ChatbotSetupScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
            self.isUpdating = true
            defer {
                self.isUpdating = false
            }
            
            if self.component == nil {
                if let bot = component.initialData.bot, let botPeer = component.initialData.botPeer, let addressName = botPeer.addressName {
                    self.botResolutionState = BotResolutionState(query: addressName, state: .found(peer: botPeer, isInstalled: true))
                    self.resetQueryText = addressName.lowercased()
                    
                    self.replyToMessages = bot.canReply
                    
                    let initialRecipients = bot.recipients
                    
                    var mappedCategories = Set<AdditionalPeerList.Category>()
                    if initialRecipients.categories.contains(.existingChats) {
                        mappedCategories.insert(.existingChats)
                    }
                    if initialRecipients.categories.contains(.newChats) {
                        mappedCategories.insert(.newChats)
                    }
                    if initialRecipients.categories.contains(.contacts) {
                        mappedCategories.insert(.contacts)
                    }
                    if initialRecipients.categories.contains(.nonContacts) {
                        mappedCategories.insert(.nonContacts)
                    }
                    
                    var additionalPeers: [AdditionalPeerList.Peer] = []
                    for peerId in initialRecipients.additionalPeers {
                        if let peer = component.initialData.additionalPeers[peerId] {
                            additionalPeers.append(peer)
                        }
                    }
                    
                    var excludePeers: [AdditionalPeerList.Peer] = []
                    for peerId in initialRecipients.excludePeers {
                        if let peer = component.initialData.additionalPeers[peerId] {
                            excludePeers.append(peer)
                        }
                    }
                    
                    self.additionalPeerList = AdditionalPeerList(
                        categories: mappedCategories,
                        peers: additionalPeers,
                        excludePeers: excludePeers
                    )
                    
                    self.hasAccessToAllChatsByDefault = initialRecipients.exclude
                }
            }
            
            let environment = environment[EnvironmentType.self].value
            let themeUpdated = self.environment?.theme !== environment.theme
            self.environment = environment
            
            self.component = component
            self.state = state
            
            if themeUpdated {
                self.backgroundColor = environment.theme.list.blocksBackgroundColor
            }
            
            let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
            
            let navigationTitleSize = self.navigationTitle.update(
                transition: transition,
                component: AnyComponent(MultilineTextComponent(
                    text: .plain(NSAttributedString(string: environment.strings.ChatbotSetup_TitleItem, font: Font.semibold(17.0), textColor: environment.theme.rootController.navigationBar.primaryTextColor)),
                    horizontalAlignment: .center
                )),
                environment: {},
                containerSize: CGSize(width: availableSize.width, height: 100.0)
            )
            let navigationTitleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - navigationTitleSize.width) / 2.0), y: environment.statusBarHeight + floor((environment.navigationHeight - environment.statusBarHeight - navigationTitleSize.height) / 2.0)), size: navigationTitleSize)
            if let navigationTitleView = self.navigationTitle.view {
                if navigationTitleView.superview == nil {
                    if let controller = self.environment?.controller(), let navigationBar = controller.navigationBar {
                        navigationBar.view.addSubview(navigationTitleView)
                    }
                }
                transition.setFrame(view: navigationTitleView, frame: navigationTitleFrame)
            }
            
            let bottomContentInset: CGFloat = 24.0
            let sideInset: CGFloat = 16.0 + environment.safeInsets.left
            let sectionSpacing: CGFloat = 32.0
            
            let _ = bottomContentInset
            let _ = sectionSpacing
            
            var contentHeight: CGFloat = 0.0
            
            contentHeight += environment.navigationHeight
            
            let iconSize = self.icon.update(
                transition: .immediate,
                component: AnyComponent(LottieComponent(
                    content: LottieComponent.AppBundleContent(name: "BotEmoji"),
                    loop: false
                )),
                environment: {},
                containerSize: CGSize(width: 100.0, height: 100.0)
            )
            let iconFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - iconSize.width) * 0.5), y: contentHeight + 8.0), size: iconSize)
            if let iconView = self.icon.view as? LottieComponent.View {
                if iconView.superview == nil {
                    self.scrollView.addSubview(iconView)
                    iconView.playOnce()
                }
                transition.setPosition(view: iconView, position: iconFrame.center)
                iconView.bounds = CGRect(origin: CGPoint(), size: iconFrame.size)
            }
            
            contentHeight += 129.0
            
            let subtitleString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(environment.strings.ChatbotSetup_Text, attributes: MarkdownAttributes(
                body: MarkdownAttributeSet(font: Font.regular(15.0), textColor: environment.theme.list.freeTextColor),
                bold: MarkdownAttributeSet(font: Font.semibold(15.0), textColor: environment.theme.list.freeTextColor),
                link: MarkdownAttributeSet(font: Font.regular(15.0), textColor: environment.theme.list.itemAccentColor),
                linkAttribute: { attributes in
                    return ("URL", "")
                }), textAlignment: .center
            ))
            if self.chevronImage == nil {
                self.chevronImage = UIImage(bundleImageName: "Settings/TextArrowRight")
            }
            if let range = subtitleString.string.range(of: ">"), let chevronImage = self.chevronImage {
                subtitleString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: subtitleString.string))
            }
            
            let subtitleSize = self.subtitle.update(
                transition: .immediate,
                component: AnyComponent(BalancedTextComponent(
                    text: .plain(subtitleString),
                    horizontalAlignment: .center,
                    maximumNumberOfLines: 0,
                    lineSpacing: 0.25,
                    highlightColor: environment.theme.list.itemAccentColor.withMultipliedAlpha(0.1),
                    highlightAction: { attributes in
                        if let _ = attributes[NSAttributedString.Key(rawValue: "URL")] {
                            return NSAttributedString.Key(rawValue: "URL")
                        } else {
                            return nil
                        }
                    },
                    tapAction: { [weak self] _, _ in
                        guard let self, let component = self.component, let environment = self.environment else {
                            return
                        }
                        component.context.sharedContext.applicationBindings.openUrl(environment.strings.ChatbotSetup_TextLink)
                    }
                )),
                environment: {},
                containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 1000.0)
            )
            let subtitleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - subtitleSize.width) * 0.5), y: contentHeight), size: subtitleSize)
            if let subtitleView = self.subtitle.view {
                if subtitleView.superview == nil {
                    self.scrollView.addSubview(subtitleView)
                }
                transition.setPosition(view: subtitleView, position: subtitleFrame.center)
                subtitleView.bounds = CGRect(origin: CGPoint(), size: subtitleFrame.size)
            }
            contentHeight += subtitleSize.height
            contentHeight += 27.0
            
            let resetQueryText = self.resetQueryText
            self.resetQueryText = nil
            var nameSectionItems: [AnyComponentWithIdentity<Empty>] = []
            nameSectionItems.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(ListTextFieldItemComponent(
                theme: environment.theme,
                initialText: "",
                resetText: resetQueryText.flatMap { ListTextFieldItemComponent.ResetText(value: $0) },
                placeholder: environment.strings.ChatbotSetup_BotSearchPlaceholder,
                autocapitalizationType: .none,
                autocorrectionType: .no,
                updated: { [weak self] value in
                    guard let self else {
                        return
                    }
                    self.updateBotQuery(query: value)
                },
                tag: self.textFieldTag
            ))))
            if let botResolutionState = self.botResolutionState {
                let mappedContent: ChatbotSearchResultItemComponent.Content
                switch botResolutionState.state {
                case .searching:
                    mappedContent = .searching
                case .notFound:
                    mappedContent = .notFound
                case let .found(peer, isInstalled):
                    mappedContent = .found(peer: peer, isInstalled: isInstalled)
                }
                nameSectionItems.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(ChatbotSearchResultItemComponent(
                    context: component.context,
                    theme: environment.theme,
                    strings: environment.strings,
                    content: mappedContent,
                    installAction: { [weak self] in
                        guard let self else {
                            return
                        }
                        self.endEditing(true)
                        
                        if var botResolutionState = self.botResolutionState, case let .found(peer, isInstalled) = botResolutionState.state, !isInstalled {
                            if case let .user(user) = peer, let botInfo = user.botInfo, botInfo.flags.contains(.isBusiness) {
                                botResolutionState.state = .found(peer: peer, isInstalled: true)
                                self.botResolutionState = botResolutionState
                                self.state?.updated(transition: .spring(duration: 0.3))
                            } else {
                                self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.ChatbotSetup_ErrorBotNotBusinessCapable, actions: [
                                    TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
                                    })
                                ]), in: .window(.root))
                            }
                        }
                    },
                    removeAction: { [weak self] in
                        guard let self else {
                            return
                        }
                        if let botResolutionState = self.botResolutionState, case let .found(_, isInstalled) = botResolutionState.state, isInstalled {
                            self.botResolutionState = nil
                            if let botResolutionDisposable = self.botResolutionDisposable {
                                self.botResolutionDisposable = nil
                                botResolutionDisposable.dispose()
                            }
                            
                            if let textFieldView = self.nameSection.findTaggedView(tag: self.textFieldTag) as? ListTextFieldItemComponent.View {
                                textFieldView.setText(text: "", updateState: false)
                            }
                            self.state?.updated(transition: .spring(duration: 0.3))
                        }
                    }
                ))))
            }
            
            let nameSectionSize = self.nameSection.update(
                transition: transition,
                component: AnyComponent(ListSectionComponent(
                    theme: environment.theme,
                    header: nil,
                    footer: AnyComponent(MultilineTextComponent(
                        text: .plain(NSAttributedString(
                            string: environment.strings.ChatbotSetup_BotSectionFooter,
                            font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
                            textColor: environment.theme.list.freeTextColor
                        )),
                        maximumNumberOfLines: 0
                    )),
                    items: nameSectionItems
                )),
                environment: {},
                containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 10000.0)
            )
            let nameSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: nameSectionSize)
            if let nameSectionView = self.nameSection.view {
                if nameSectionView.superview == nil {
                    self.scrollView.addSubview(nameSectionView)
                }
                transition.setFrame(view: nameSectionView, frame: nameSectionFrame)
            }
            contentHeight += nameSectionSize.height
            contentHeight += sectionSpacing
            
            let accessSectionSize = self.accessSection.update(
                transition: transition,
                component: AnyComponent(ListSectionComponent(
                    theme: environment.theme,
                    header: AnyComponent(MultilineTextComponent(
                        text: .plain(NSAttributedString(
                            string: environment.strings.ChatbotSetup_RecipientsSectionHeader,
                            font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
                            textColor: environment.theme.list.freeTextColor
                        )),
                        maximumNumberOfLines: 0
                    )),
                    footer: nil,
                    items: [
                        AnyComponentWithIdentity(id: 0, component: AnyComponent(ListActionItemComponent(
                            theme: environment.theme,
                            title: AnyComponent(VStack([
                                AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
                                    text: .plain(NSAttributedString(
                                        string: environment.strings.BusinessMessageSetup_RecipientsOptionAllExcept,
                                        font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
                                        textColor: environment.theme.list.itemPrimaryTextColor
                                    )),
                                    maximumNumberOfLines: 1
                                ))),
                            ], alignment: .left, spacing: 2.0)),
                            leftIcon: .custom(AnyComponentWithIdentity(id: 0, component: AnyComponent(Image(
                                image: checkIcon,
                                tintColor: !self.hasAccessToAllChatsByDefault ? .clear : environment.theme.list.itemAccentColor,
                                contentMode: .center
                            ))), false),
                            accessory: nil,
                            action: { [weak self] _ in
                                guard let self else {
                                    return
                                }
                                if !self.hasAccessToAllChatsByDefault {
                                    self.hasAccessToAllChatsByDefault = true
                                    self.additionalPeerList.categories.removeAll()
                                    self.additionalPeerList.peers.removeAll()
                                    self.state?.updated(transition: .immediate)
                                }
                            }
                        ))),
                        AnyComponentWithIdentity(id: 1, component: AnyComponent(ListActionItemComponent(
                            theme: environment.theme,
                            title: AnyComponent(VStack([
                                AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
                                    text: .plain(NSAttributedString(
                                        string: environment.strings.BusinessMessageSetup_RecipientsOptionOnly,
                                        font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
                                        textColor: environment.theme.list.itemPrimaryTextColor
                                    )),
                                    maximumNumberOfLines: 1
                                ))),
                            ], alignment: .left, spacing: 2.0)),
                            leftIcon: .custom(AnyComponentWithIdentity(id: 0, component: AnyComponent(Image(
                                image: checkIcon,
                                tintColor: self.hasAccessToAllChatsByDefault ? .clear : environment.theme.list.itemAccentColor,
                                contentMode: .center
                            ))), false),
                            accessory: nil,
                            action: { [weak self] _ in
                                guard let self else {
                                    return
                                }
                                if self.hasAccessToAllChatsByDefault {
                                    self.hasAccessToAllChatsByDefault = false
                                    self.additionalPeerList.categories.removeAll()
                                    self.additionalPeerList.peers.removeAll()
                                    self.state?.updated(transition: .immediate)
                                }
                            }
                        )))
                    ]
                )),
                environment: {},
                containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 10000.0)
            )
            let accessSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: accessSectionSize)
            if let accessSectionView = self.accessSection.view {
                if accessSectionView.superview == nil {
                    self.scrollView.addSubview(accessSectionView)
                }
                transition.setFrame(view: accessSectionView, frame: accessSectionFrame)
            }
            contentHeight += accessSectionSize.height
            contentHeight += sectionSpacing
            
            let categoriesAndUsersItemCount = self.additionalPeerList.categories.count + self.additionalPeerList.peers.count
            let excludedSectionValue: String
            if categoriesAndUsersItemCount == 0 {
                excludedSectionValue = environment.strings.ChatbotSetup_RecipientSummary_ValueEmpty
            } else {
                excludedSectionValue = environment.strings.ChatbotSetup_RecipientSummary_ValueItems(Int32(categoriesAndUsersItemCount))
            }
            
            let excludedUsersItemCount = self.additionalPeerList.excludePeers.count
            let excludedUsersValue: String
            if excludedUsersItemCount == 0 {
                excludedUsersValue = environment.strings.ChatbotSetup_RecipientSummary_ValueEmpty
            } else {
                excludedUsersValue = environment.strings.ChatbotSetup_RecipientSummary_ValueItems(Int32(excludedUsersItemCount))
            }
            
            var excludedSectionItems: [AnyComponentWithIdentity<Empty>] = []
            excludedSectionItems.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(ListActionItemComponent(
                theme: environment.theme,
                title: AnyComponent(VStack([
                    AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
                        text: .plain(NSAttributedString(
                            string: self.hasAccessToAllChatsByDefault ? environment.strings.ChatbotSetup_RecipientSummary_ExcludedChatsItem : environment.strings.ChatbotSetup_RecipientSummary_IncludedChatsItem,
                            font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
                            textColor: environment.theme.list.itemPrimaryTextColor
                        )),
                        maximumNumberOfLines: 1
                    ))),
                ], alignment: .left, spacing: 2.0)),
                leftIcon: nil,
                icon: ListActionItemComponent.Icon(component: AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent(
                    text: .plain(NSAttributedString(
                        string: excludedSectionValue,
                        font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
                        textColor: environment.theme.list.itemSecondaryTextColor
                    )),
                    maximumNumberOfLines: 1
                )))),
                accessory: .arrow,
                action: { [weak self] _ in
                    guard let self else {
                        return
                    }
                    self.openAdditionalPeerListSetup(isExclude: false)
                }
            ))))
            
            let excludedSectionSize = self.excludedSection.update(
                transition: transition,
                component: AnyComponent(ListSectionComponent(
                    theme: environment.theme,
                    header: nil,
                    footer: AnyComponent(MultilineTextComponent(
                        text: .markdown(
                            text: self.hasAccessToAllChatsByDefault ? environment.strings.ChatbotSetup_Recipients_ExcludedSectionFooter : environment.strings.ChatbotSetup_Recipients_IncludedSectionFooter,
                            attributes: MarkdownAttributes(
                                body: MarkdownAttributeSet(font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), textColor: environment.theme.list.freeTextColor),
                                bold: MarkdownAttributeSet(font: Font.semibold(presentationData.listsFontSize.itemListBaseHeaderFontSize), textColor: environment.theme.list.freeTextColor),
                                link: MarkdownAttributeSet(font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), textColor: environment.theme.list.itemAccentColor),
                                linkAttribute: { _ in
                                    return nil
                                }
                            )
                        ),
                        maximumNumberOfLines: 0
                    )),
                    items: excludedSectionItems
                )),
                environment: {},
                containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 10000.0)
            )
            let excludedSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: excludedSectionSize)
            if let excludedSectionView = self.excludedSection.view {
                if excludedSectionView.superview == nil {
                    self.scrollView.addSubview(excludedSectionView)
                }
                transition.setFrame(view: excludedSectionView, frame: excludedSectionFrame)
            }
            contentHeight += excludedSectionSize.height
            contentHeight += sectionSpacing
            
            var excludedUsersContentHeight: CGFloat = 0.0
            var excludedUsersSectionItems: [AnyComponentWithIdentity<Empty>] = []
            excludedUsersSectionItems.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(ListActionItemComponent(
                theme: environment.theme,
                title: AnyComponent(VStack([
                    AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
                        text: .plain(NSAttributedString(
                            string: environment.strings.ChatbotSetup_RecipientSummary_ExcludedChatsItem,
                            font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
                            textColor: environment.theme.list.itemPrimaryTextColor
                        )),
                        maximumNumberOfLines: 1
                    ))),
                ], alignment: .left, spacing: 2.0)),
                leftIcon: nil,
                icon: ListActionItemComponent.Icon(component: AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent(
                    text: .plain(NSAttributedString(
                        string: excludedUsersValue,
                        font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
                        textColor: environment.theme.list.itemSecondaryTextColor
                    )),
                    maximumNumberOfLines: 1
                )))),
                accessory: .arrow,
                action: { [weak self] _ in
                    guard let self else {
                        return
                    }
                    self.openAdditionalPeerListSetup(isExclude: true)
                }
            ))))
            let excludedUsersSectionSize = self.excludedUsersSection.update(
                transition: transition,
                component: AnyComponent(ListSectionComponent(
                    theme: environment.theme,
                    header: nil,
                    footer: AnyComponent(MultilineTextComponent(
                        text: .markdown(
                            text: environment.strings.ChatbotSetup_Recipients_ExcludedSectionFooter,
                            attributes: MarkdownAttributes(
                                body: MarkdownAttributeSet(font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), textColor: environment.theme.list.freeTextColor),
                                bold: MarkdownAttributeSet(font: Font.semibold(presentationData.listsFontSize.itemListBaseHeaderFontSize), textColor: environment.theme.list.freeTextColor),
                                link: MarkdownAttributeSet(font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), textColor: environment.theme.list.itemAccentColor),
                                linkAttribute: { _ in
                                    return nil
                                }
                            )
                        ),
                        maximumNumberOfLines: 0
                    )),
                    items: excludedUsersSectionItems
                )),
                environment: {},
                containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 10000.0)
            )
            let excludedUsersSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight + excludedUsersContentHeight), size: excludedSectionSize)
            if let excludedUsersSectionView = self.excludedUsersSection.view {
                if excludedUsersSectionView.superview == nil {
                    self.scrollView.addSubview(excludedUsersSectionView)
                }
                transition.setFrame(view: excludedUsersSectionView, frame: excludedUsersSectionFrame)
                transition.setAlpha(view: excludedUsersSectionView, alpha: !self.hasAccessToAllChatsByDefault ? 1.0 : 0.0)
            }
            excludedUsersContentHeight += excludedUsersSectionSize.height
            excludedUsersContentHeight += sectionSpacing
            if !self.hasAccessToAllChatsByDefault {
                contentHeight += excludedUsersContentHeight
            }
            
            let permissionsSectionSize = self.permissionsSection.update(
                transition: transition,
                component: AnyComponent(ListSectionComponent(
                    theme: environment.theme,
                    header: AnyComponent(MultilineTextComponent(
                        text: .plain(NSAttributedString(
                            string: environment.strings.ChatbotSetup_PermissionsSectionHeader,
                            font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
                            textColor: environment.theme.list.freeTextColor
                        )),
                        maximumNumberOfLines: 0
                    )),
                    footer: AnyComponent(MultilineTextComponent(
                        text: .plain(NSAttributedString(
                            string: environment.strings.ChatbotSetup_PermissionsSectionFooter,
                            font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
                            textColor: environment.theme.list.freeTextColor
                        )),
                        maximumNumberOfLines: 0
                    )),
                    items: [
                        AnyComponentWithIdentity(id: 0, component: AnyComponent(ListActionItemComponent(
                            theme: environment.theme,
                            title: AnyComponent(VStack([
                                AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
                                    text: .plain(NSAttributedString(
                                        string: environment.strings.ChatbotSetup_Permission_ReplyToMessages,
                                        font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
                                        textColor: environment.theme.list.itemPrimaryTextColor
                                    )),
                                    maximumNumberOfLines: 1
                                ))),
                            ], alignment: .left, spacing: 2.0)),
                            accessory: .toggle(ListActionItemComponent.Toggle(style: .icons, isOn: self.replyToMessages, action: { [weak self] _ in
                                guard let self else {
                                    return
                                }
                                self.replyToMessages = !self.replyToMessages
                                self.state?.updated(transition: .spring(duration: 0.4))
                            })),
                            action: nil
                        ))),
                    ]
                )),
                environment: {},
                containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 10000.0)
            )
            let permissionsSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: permissionsSectionSize)
            if let permissionsSectionView = self.permissionsSection.view {
                if permissionsSectionView.superview == nil {
                    self.scrollView.addSubview(permissionsSectionView)
                }
                transition.setFrame(view: permissionsSectionView, frame: permissionsSectionFrame)
            }
            contentHeight += permissionsSectionSize.height
            
            contentHeight += bottomContentInset
            contentHeight += environment.safeInsets.bottom
            
            let previousBounds = self.scrollView.bounds
            
            let contentSize = CGSize(width: availableSize.width, height: contentHeight)
            if self.scrollView.frame != CGRect(origin: CGPoint(), size: availableSize) {
                self.scrollView.frame = CGRect(origin: CGPoint(), size: availableSize)
            }
            if self.scrollView.contentSize != contentSize {
                self.scrollView.contentSize = contentSize
            }
            let scrollInsets = UIEdgeInsets(top: environment.navigationHeight, left: 0.0, bottom: 0.0, right: 0.0)
            if self.scrollView.scrollIndicatorInsets != scrollInsets {
                self.scrollView.scrollIndicatorInsets = scrollInsets
            }
                        
            if !previousBounds.isEmpty, !transition.animation.isImmediate {
                let bounds = self.scrollView.bounds
                if bounds.maxY != previousBounds.maxY {
                    let offsetY = previousBounds.maxY - bounds.maxY
                    transition.animateBoundsOrigin(view: self.scrollView, from: CGPoint(x: 0.0, y: offsetY), to: CGPoint(), additive: true)
                }
            }
            
            self.topOverscrollLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: -3000.0), size: CGSize(width: availableSize.width, height: 3000.0))
            
            self.updateScrolling(transition: transition)
            
            return availableSize
        }
    }
    
    func makeView() -> View {
        return View()
    }
    
    func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
        return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
    }
}

public final class ChatbotSetupScreen: ViewControllerComponentContainer {
    public final class InitialData: ChatbotSetupScreenInitialData {
        fileprivate let bot: TelegramAccountConnectedBot?
        fileprivate let botPeer: EnginePeer?
        fileprivate let additionalPeers: [EnginePeer.Id: ChatbotSetupScreenComponent.AdditionalPeerList.Peer]
        
        fileprivate init(
            bot: TelegramAccountConnectedBot?,
            botPeer: EnginePeer?,
            additionalPeers: [EnginePeer.Id: ChatbotSetupScreenComponent.AdditionalPeerList.Peer]
        ) {
            self.bot = bot
            self.botPeer = botPeer
            self.additionalPeers = additionalPeers
        }
    }
    
    private let context: AccountContext
    
    public init(context: AccountContext, initialData: InitialData) {
        self.context = context
        
        super.init(context: context, component: ChatbotSetupScreenComponent(
            context: context,
            initialData: initialData
        ), navigationBarAppearance: .default, theme: .default, updatedPresentationData: nil)
        
        let presentationData = context.sharedContext.currentPresentationData.with { $0 }
        self.title = ""
        self.navigationItem.backBarButtonItem = UIBarButtonItem(title: presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
        
        self.scrollToTop = { [weak self] in
            guard let self, let componentView = self.node.hostView.componentView as? ChatbotSetupScreenComponent.View else {
                return
            }
            componentView.scrollToTop()
        }
        
        self.attemptNavigation = { [weak self] complete in
            guard let self, let componentView = self.node.hostView.componentView as? ChatbotSetupScreenComponent.View else {
                return true
            }
            
            return componentView.attemptNavigation(complete: complete)
        }
    }
    
    required public init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    deinit {
    }
    
    @objc private func cancelPressed() {
        self.dismiss()
    }
    
    override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
        super.containerLayoutUpdated(layout, transition: transition)
    }
    
    public static func initialData(context: AccountContext) -> Signal<ChatbotSetupScreenInitialData, NoError> {
        return context.engine.data.get(
            TelegramEngine.EngineData.Item.Peer.BusinessConnectedBot(id: context.account.peerId)
        )
        |> mapToSignal { connectedBot -> Signal<ChatbotSetupScreenInitialData, NoError> in
            guard let connectedBot else {
                return .single(
                    InitialData(
                        bot: nil,
                        botPeer: nil,
                        additionalPeers: [:]
                    )
                )
            }
            
            var additionalPeerIds = Set<EnginePeer.Id>()
            additionalPeerIds.formUnion(connectedBot.recipients.additionalPeers)
            additionalPeerIds.formUnion(connectedBot.recipients.excludePeers)
            
            return context.engine.data.get(
                TelegramEngine.EngineData.Item.Peer.Peer(id: connectedBot.id),
                EngineDataMap(additionalPeerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:))),
                EngineDataMap(additionalPeerIds.map(TelegramEngine.EngineData.Item.Peer.IsContact.init(id:)))
            )
            |> map { botPeer, peers, isContacts -> ChatbotSetupScreenInitialData in
                var additionalPeers: [EnginePeer.Id: ChatbotSetupScreenComponent.AdditionalPeerList.Peer] = [:]
                for id in additionalPeerIds {
                    guard let peer = peers[id], let peer else {
                        continue
                    }
                    additionalPeers[id] = ChatbotSetupScreenComponent.AdditionalPeerList.Peer(
                        peer: peer,
                        isContact: isContacts[id] ?? false
                    )
                }
                
                return InitialData(
                    bot: connectedBot,
                    botPeer: botPeer,
                    additionalPeers: additionalPeers
                )
            }
        }
    }
}