import Foundation
import UIKit
import AsyncDisplayKit
import Display
import ComponentFlow
import SwiftSignalKit
import Postbox
import TelegramCore
import Markdown
import TextFormat
import TelegramPresentationData
import ViewControllerComponent
import SheetComponent
import BalancedTextComponent
import MultilineTextComponent
import BundleIconComponent
import ButtonComponent
import ItemListUI
import AccountContext
import PresentationDataUtils
import ListSectionComponent
import ListItemComponentAdaptor
import TelegramStringFormatting
import UndoUI

private final class SheetContent: CombinedComponent {
    typealias EnvironmentType = ViewControllerComponentContainer.Environment
    
    let context: AccountContext
    let botName: String
    let botAddress: String
    let preparedMessage: PreparedInlineMessage
    let dismiss: () -> Void
    
    init(
        context: AccountContext,
        botName: String,
        botAddress: String,
        preparedMessage: PreparedInlineMessage,
        dismiss: @escaping () -> Void
    ) {
        self.context = context
        self.botName = botName
        self.botAddress = botAddress
        self.preparedMessage = preparedMessage
        self.dismiss = dismiss
    }
    
    static func ==(lhs: SheetContent, rhs: SheetContent) -> Bool {
        if lhs.context !== rhs.context {
            return false
        }
        return true
    }
    
    static var body: Body {
        let closeButton = Child(Button.self)
        let title = Child(Text.self)
        let amountSection = Child(ListSectionComponent.self)
        let button = Child(ButtonComponent.self)
        
        return { context in
            let environment = context.environment[EnvironmentType.self]
            let component = context.component
            
            let controller = environment.controller
            
            let theme = environment.theme.withModalBlocksBackground()
            let strings = environment.strings
            let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
            
            let sideInset: CGFloat = 16.0
            var contentSize = CGSize(width: context.availableSize.width, height: 18.0)
            
            let constrainedTitleWidth = context.availableSize.width - 16.0 * 2.0
            
            let closeButton = closeButton.update(
                component: Button(
                    content: AnyComponent(Text(text: environment.strings.Common_Cancel, font: Font.regular(17.0), color: theme.actionSheet.controlAccentColor)),
                    action: {
                        component.dismiss()
                    }
                ),
                availableSize: CGSize(width: 120.0, height: 30.0),
                transition: .immediate
            )
            context.add(closeButton
                .position(CGPoint(x: closeButton.size.width / 2.0 + sideInset, y: 28.0))
            )
                    
            let title = title.update(
                component: Text(text: environment.strings.WebApp_ShareMessage_Title, font: Font.bold(17.0), color: theme.list.itemPrimaryTextColor),
                availableSize: CGSize(width: constrainedTitleWidth, height: context.availableSize.height),
                transition: .immediate
            )
            context.add(title
                .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + title.size.height / 2.0))
            )
            contentSize.height += title.size.height
            contentSize.height += 40.0
                        
            let amountFont = Font.regular(13.0)
            let amountTextColor = theme.list.freeTextColor
            let amountMarkdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: amountFont, textColor: amountTextColor), bold: MarkdownAttributeSet(font: amountFont, textColor: amountTextColor), link: MarkdownAttributeSet(font: amountFont, textColor: theme.list.itemAccentColor), linkAttribute: { contents in
                return (TelegramTextAttributes.URL, contents)
            })

            let amountInfoString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(environment.strings.WebApp_ShareMessage_Info(component.botName).string, attributes: amountMarkdownAttributes, textAlignment: .natural))
            let amountFooter = AnyComponent(MultilineTextComponent(
                text: .plain(amountInfoString),
                maximumNumberOfLines: 0,
                highlightColor: environment.theme.list.itemAccentColor.withAlphaComponent(0.1),
                highlightInset: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: -8.0),
                highlightAction: { attributes in
                    if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
                        return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)
                    } else {
                        return nil
                    }
                },
                tapAction: { attributes, _ in
                    if let controller = controller() as? WebAppMessagePreviewScreen, let navigationController = controller.navigationController as? NavigationController {
                        component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: strings.Stars_PaidContent_AmountInfo_URL, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {})
                    }
                }
            ))
                        
            var text: String = ""
            var entities: TextEntitiesMessageAttribute?
            var media: [Media] = []
            
            switch component.preparedMessage.result {
            case let .internalReference(reference):
                switch reference.message {
                case let .auto(textValue, entitiesValue, _):
                    text = textValue
                    entities = entitiesValue
                    if let file = reference.file {
                        media = [file]
                    } else if let image = reference.image {
                        media = [image]
                    }
                case let .text(textValue, entitiesValue, disableUrlPreview, previewParameters, _):
                    text = textValue
                    entities = entitiesValue
                    let _ = disableUrlPreview
                    let _ = previewParameters
                case let .contact(contact, _):
                    media = [contact]
                case let .mapLocation(map, _):
                    media = [map]
                case let .invoice(invoice, _):
                    media = [invoice]
                default:
                    break
                }
            case let .externalReference(reference):
                switch reference.message {
                case let .auto(textValue, entitiesValue, _):
                    text = textValue
                    entities = entitiesValue
                    if let content = reference.content {
                        media = [content]
                    }
                case let .text(textValue, entitiesValue, disableUrlPreview, previewParameters, _):
                    text = textValue
                    entities = entitiesValue
                    let _ = disableUrlPreview
                    let _ = previewParameters
                case let .contact(contact, _):
                    media = [contact]
                case let .mapLocation(map, _):
                    media = [map]
                case let .invoice(invoice, _):
                    media = [invoice]
                default:
                    break
                }
            }
            
            let messageItem = PeerNameColorChatPreviewItem.MessageItem(
                text: text,
                entities: entities,
                media: media,
                botAddress: component.botAddress
            )
                     
            let listItemParams = ListViewItemLayoutParams(width: context.availableSize.width - sideInset * 2.0, leftInset: 0.0, rightInset: 0.0, availableHeight: 10000.0, isStandalone: true)
            
            let amountSection = amountSection.update(
                component: ListSectionComponent(
                    theme: theme,
                    header: AnyComponent(MultilineTextComponent(
                        text: .plain(NSAttributedString(
                            string: environment.strings.WebApp_ShareMessage_PreviewTitle.uppercased(),
                            font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
                            textColor: theme.list.freeTextColor
                        )),
                        maximumNumberOfLines: 0
                    )),
                    footer: amountFooter,
                    items: [
                        AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemComponentAdaptor(
                            itemGenerator: PeerNameColorChatPreviewItem(
                                context: component.context,
                                theme: environment.theme,
                                componentTheme: environment.theme,
                                strings: environment.strings,
                                sectionId: 0,
                                fontSize: presentationData.chatFontSize,
                                chatBubbleCorners: presentationData.chatBubbleCorners,
                                wallpaper: presentationData.chatWallpaper,
                                dateTimeFormat: environment.dateTimeFormat,
                                nameDisplayOrder: presentationData.nameDisplayOrder,
                                messageItems: [messageItem]
                            ),
                            params: listItemParams
                        )))
                    ]
                ),
                environment: {},
                availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: .greatestFiniteMagnitude),
                transition: context.transition
            )
            context.add(amountSection
                .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + amountSection.size.height / 2.0))
                .clipsToBounds(true)
                .cornerRadius(10.0)
            )
            contentSize.height += amountSection.size.height
            contentSize.height += 32.0
            
            let buttonString: String = environment.strings.WebApp_ShareMessage_Share
            let buttonAttributedString = NSMutableAttributedString(string: buttonString, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
            
            let button = button.update(
                component: ButtonComponent(
                    background: ButtonComponent.Background(
                        color: theme.list.itemCheckColors.fillColor,
                        foreground: theme.list.itemCheckColors.foregroundColor,
                        pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9),
                        cornerRadius: 10.0
                    ),
                    content: AnyComponentWithIdentity(
                        id: AnyHashable(0),
                        component: AnyComponent(MultilineTextComponent(text: .plain(buttonAttributedString)))
                    ),
                    isEnabled: true,
                    displaysProgress: false,
                    action: {
                        if let controller = controller() as? WebAppMessagePreviewScreen {
                            controller.proceed()
                        }
                    }
                ),
                availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50),
                transition: .immediate
            )
            context.add(button
                .clipsToBounds(true)
                .cornerRadius(10.0)
                .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + button.size.height / 2.0))
            )
            contentSize.height += button.size.height
            contentSize.height += 15.0
            
            contentSize.height += max(environment.inputHeight, environment.safeInsets.bottom)

            return contentSize
        }
    }
    
    final class State: ComponentState {
        var cachedCloseImage: (UIImage, PresentationTheme)?
    }
    
    func makeState() -> State {
        return State()
    }
}

private final class WebAppMessagePreviewSheetComponent: CombinedComponent {
    typealias EnvironmentType = ViewControllerComponentContainer.Environment
    
    private let context: AccountContext
    private let botName: String
    private let botAddress: String
    private let preparedMessage: PreparedInlineMessage
    
    init(
        context: AccountContext,
        botName: String,
        botAddress: String,
        preparedMessage: PreparedInlineMessage
    ) {
        self.context = context
        self.botName = botName
        self.botAddress = botAddress
        self.preparedMessage = preparedMessage
    }
    
    static func ==(lhs: WebAppMessagePreviewSheetComponent, rhs: WebAppMessagePreviewSheetComponent) -> Bool {
        if lhs.context !== rhs.context {
            return false
        }
        return true
    }
        
    static var body: Body {
        let sheet = Child(SheetComponent<(EnvironmentType)>.self)
        let animateOut = StoredActionSlot(Action<Void>.self)
        
        return { context in
            let environment = context.environment[EnvironmentType.self]
            
            let controller = environment.controller
            
            let sheet = sheet.update(
                component: SheetComponent<EnvironmentType>(
                    content: AnyComponent<EnvironmentType>(SheetContent(
                        context: context.component.context,
                        botName: context.component.botName,
                        botAddress: context.component.botAddress,
                        preparedMessage: context.component.preparedMessage,
                        dismiss: {
                            animateOut.invoke(Action { _ in
                                if let controller = controller() as? WebAppMessagePreviewScreen {
                                    controller.completeWithResult(false)
                                    controller.dismiss(completion: nil)
                                }
                            })
                        }
                    )),
                    backgroundColor: .color(environment.theme.list.blocksBackgroundColor),
                    followContentSizeChanges: false,
                    clipsContent: true,
                    isScrollEnabled: false,
                    animateOut: animateOut
                ),
                environment: {
                    environment
                    SheetComponentEnvironment(
                        isDisplaying: environment.value.isVisible,
                        isCentered: environment.metrics.widthClass == .regular,
                        hasInputHeight: !environment.inputHeight.isZero,
                        regularMetricsSize: CGSize(width: 430.0, height: 900.0),
                        dismiss: { animated in
                            if animated {
                                animateOut.invoke(Action { _ in
                                    if let controller = controller() as? WebAppMessagePreviewScreen {
                                        controller.completeWithResult(false)
                                        controller.dismiss(completion: nil)
                                    }
                                })
                            } else {
                                if let controller = controller() as? WebAppMessagePreviewScreen {
                                    controller.completeWithResult(false)
                                    controller.dismiss(completion: nil)
                                }
                            }
                        }
                    )
                },
                availableSize: context.availableSize,
                transition: context.transition
            )
            
            context.add(sheet
                .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
            )
            
            return context.availableSize
        }
    }
}

public final class WebAppMessagePreviewScreen: ViewControllerComponentContainer {
    private let context: AccountContext
    private let preparedMessage: PreparedInlineMessage
    fileprivate let completion: (Bool) -> Void
        
    public init(
        context: AccountContext,
        botName: String,
        botAddress: String,
        preparedMessage: PreparedInlineMessage,
        completion: @escaping (Bool) -> Void
    ) {
        self.context = context
        self.preparedMessage = preparedMessage
        self.completion = completion
        
        super.init(
            context: context,
            component: WebAppMessagePreviewSheetComponent(
                context: context,
                botName: botName,
                botAddress: botAddress,
                preparedMessage: preparedMessage
            ),
            navigationBarAppearance: .none,
            statusBarStyle: .ignore,
            theme: .default
        )
        
        self.navigationPresentation = .flatModal
    }
        
    required public init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    fileprivate func complete(peers: [EnginePeer]) {
        for peer in peers {
            let _ = self.context.engine.messages.enqueueOutgoingMessage(
                to: peer.id,
                replyTo: nil,
                storyId: nil,
                content: .preparedInlineMessage(self.preparedMessage)
            ).start()
        }
        
        let text: String
        let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
        if peers.count == 1, let peer = peers.first {
            let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
            text = presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string
        } else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last {
            let firstPeerName = firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
            let secondPeerName = secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
            text = presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string
        } else if let peer = peers.first {
            let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
            text = presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string
        } else {
            text = ""
        }
        
        if let navigationController = self.navigationController as? NavigationController {
            Queue.mainQueue().after(1.0) {
                guard let lastController = navigationController.viewControllers.last as? ViewController else {
                    return
                }
                lastController.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: false, text: text), elevatedLayout: false, position: .top, animateInAsReplacement: true, action: { action in
                    return false
                }), in: .window(.root))
            }
        }
        
        self.completeWithResult(true)
        self.dismiss()
    }
        
    private var completed = false
    fileprivate func completeWithResult(_ result: Bool) {
        guard !self.completed else {
            return
        }
        self.completion(result)
    }
    
    fileprivate func proceed() {
        let requestPeerType = self.preparedMessage.peerTypes.requestPeerTypes
        
        let controller = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: [.excludeRecent, .doNotSearchMessages], requestPeerType: requestPeerType, hasContactSelector: false, multipleSelection: true, selectForumThreads: true, immediatelyActivateMultipleSelection: true))
        
        controller.multiplePeersSelected = { [weak self, weak controller] peers, _, _, _, _, _ in
            guard let self else {
                return
            }
            self.complete(peers: peers)
            controller?.dismiss()
        }
        
        self.push(controller)
    }
    
    public func dismissAnimated() {
        self.completeWithResult(false)
        if let view = self.node.hostView.findTaggedView(tag: SheetComponent<ViewControllerComponentContainer.Environment>.View.Tag()) as? SheetComponent<ViewControllerComponentContainer.Environment>.View {
            view.dismissAnimated()
        }
    }
}