import Foundation
import UIKit
import Display
import ComponentFlow
import SwiftSignalKit
import TelegramCore
import AccountContext
import MultilineTextComponent
import BlurredBackgroundComponent
import Markdown
import TelegramPresentationData
import ScrollComponent

private final class LimitComponent: CombinedComponent {
    let title: String
    let titleColor: UIColor
    let text: String
    let textColor: UIColor
    let accentColor: UIColor
    let inactiveColor: UIColor
    let inactiveTextColor: UIColor
    let inactiveTitle: String
    let inactiveValue: String
    let activeColor: UIColor
    let activeTextColor: UIColor
    let activeTitle: String
    let activeValue: String
    
    public init(
        title: String,
        titleColor: UIColor,
        text: String,
        textColor: UIColor,
        accentColor: UIColor,
        inactiveColor: UIColor,
        inactiveTextColor: UIColor,
        inactiveTitle: String,
        inactiveValue: String,
        activeColor: UIColor,
        activeTextColor: UIColor,
        activeTitle: String,
        activeValue: String
    ) {
        self.title = title
        self.titleColor = titleColor
        self.text = text
        self.textColor = textColor
        self.accentColor = accentColor
        self.inactiveColor = inactiveColor
        self.inactiveTextColor = inactiveTextColor
        self.inactiveTitle = inactiveTitle
        self.inactiveValue = inactiveValue
        self.activeColor = activeColor
        self.activeTextColor = activeTextColor
        self.activeTitle = activeTitle
        self.activeValue = activeValue
    }
    
    static func ==(lhs: LimitComponent, rhs: LimitComponent) -> Bool {
        if lhs.title != rhs.title {
            return false
        }
        if lhs.titleColor != rhs.titleColor {
            return false
        }
        if lhs.text != rhs.text {
            return false
        }
        if lhs.textColor != rhs.textColor {
            return false
        }
        if lhs.accentColor != rhs.accentColor {
            return false
        }
        if lhs.inactiveColor != rhs.inactiveColor {
            return false
        }
        if lhs.inactiveTextColor != rhs.inactiveTextColor {
            return false
        }
        if lhs.inactiveTitle != rhs.inactiveTitle {
            return false
        }
        if lhs.inactiveValue != rhs.inactiveValue {
            return false
        }
        if lhs.activeColor != rhs.activeColor {
            return false
        }
        if lhs.activeTextColor != rhs.activeTextColor {
            return false
        }
        if lhs.activeTitle != rhs.activeTitle {
            return false
        }
        if lhs.activeValue != rhs.activeValue {
            return false
        }
        return true
    }
    
    static var body: Body {
        let title = Child(MultilineTextComponent.self)
        let text = Child(MultilineTextComponent.self)
        let limit = Child(PremiumLimitDisplayComponent.self)
        
        return { context in
            let component = context.component
            
            let sideInset: CGFloat = 16.0
            let textSideInset: CGFloat = sideInset + 8.0
            let spacing: CGFloat = 4.0
            
            let textTopInset: CGFloat = 9.0
            
            let title = title.update(
                component: MultilineTextComponent(
                    text: .plain(NSAttributedString(
                        string: component.title,
                        font: Font.regular(17.0),
                        textColor: component.titleColor,
                        paragraphAlignment: .natural
                    )),
                    horizontalAlignment: .center,
                    maximumNumberOfLines: 1
                ),
                availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude),
                transition: .immediate
            )
            
            let textFont = Font.regular(13.0)
            let boldTextFont = Font.semibold(13.0)
            let textColor = component.textColor
            let markdownAttributes = MarkdownAttributes(
                body: MarkdownAttributeSet(font: textFont, textColor: textColor),
                bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor),
                link: MarkdownAttributeSet(font: textFont, textColor: component.accentColor),
                linkAttribute: { _ in
                    return nil
                }
            )
                        
            let text = text.update(
                component: MultilineTextComponent(
                    text: .markdown(text: component.text, attributes: markdownAttributes),
                    horizontalAlignment: .natural,
                    maximumNumberOfLines: 0,
                    lineSpacing: 0.0
                ),
                availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
                transition: .immediate
            )
            
            let limit = limit.update(
                component: PremiumLimitDisplayComponent(
                    inactiveColor: component.inactiveColor,
                    activeColors: [component.activeColor],
                    inactiveTitle: component.inactiveTitle,
                    inactiveValue: component.inactiveValue,
                    inactiveTitleColor: component.inactiveTextColor,
                    activeTitle: component.activeTitle,
                    activeValue: component.activeValue,
                    activeTitleColor: component.activeTextColor,
                    badgeIconName: "",
                    badgeText: nil,
                    badgePosition: 0.0,
                    badgeGraphPosition: 0.5,
                    isPremiumDisabled: false
                ),
                availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: context.availableSize.height),
                transition: .immediate
            )
         
            context.add(title
                .position(CGPoint(x: textSideInset + title.size.width / 2.0, y: textTopInset + title.size.height / 2.0))
            )
            
            context.add(text
                .position(CGPoint(x: textSideInset + text.size.width / 2.0, y: textTopInset + title.size.height + spacing + text.size.height / 2.0))
            )
            
            context.add(limit
                .position(CGPoint(x: context.availableSize.width / 2.0, y: textTopInset + title.size.height + spacing + text.size.height - 20.0))
            )
        
            return CGSize(width: context.availableSize.width, height: textTopInset + title.size.height + text.size.height + 56.0)
        }
    }
}

private enum Limit: CaseIterable {
    case groups
    case pins
    case publicLinks
    case savedGifs
    case favedStickers
    case about
    case captions
    case folders
    case chatsPerFolder
    case account
    case recommendedChannels
    
    func title(strings: PresentationStrings) -> String {
        switch self {
        case .groups:
            return strings.Premium_Limits_GroupsAndChannels
        case .pins:
            return strings.Premium_Limits_PinnedChats
        case .publicLinks:
            return strings.Premium_Limits_PublicLinks
        case .savedGifs:
            return strings.Premium_Limits_SavedGifs
        case .favedStickers:
            return strings.Premium_Limits_FavedStickers
        case .about:
            return strings.Premium_Limits_Bio
        case .captions:
            return strings.Premium_Limits_Captions
        case .folders:
            return strings.Premium_Limits_Folders
        case .chatsPerFolder:
            return strings.Premium_Limits_ChatsPerFolder
        case .account:
            return strings.Premium_Limits_Accounts
        case .recommendedChannels:
            return strings.Premium_Limits_RecommendedChannels
        }
    }
    
    func text(strings: PresentationStrings) -> String {
        switch self {
            case .groups:
                return strings.Premium_Limits_GroupsAndChannelsInfo
            case .pins:
                return strings.Premium_Limits_PinnedChatsInfo
            case .publicLinks:
                return strings.Premium_Limits_PublicLinksInfo
            case .savedGifs:
                return strings.Premium_Limits_SavedGifsInfo
            case .favedStickers:
                return strings.Premium_Limits_FavedStickersInfo
            case .about:
                return strings.Premium_Limits_BioInfo
            case .captions:
                return strings.Premium_Limits_CaptionsInfo
            case .folders:
                return strings.Premium_Limits_FoldersInfo
            case .chatsPerFolder:
                return strings.Premium_Limits_ChatsPerFolderInfo
            case .account:
                return strings.Premium_Limits_AccountsInfo
            case .recommendedChannels:
                return strings.Premium_Limits_RecommendedChannelsInfo
        }
    }
    
    func limit(_ configuration: EngineConfiguration.UserLimits, isPremium: Bool) -> String {
        let value: Int32
        switch self {
            case .groups:
                value = configuration.maxChannelsCount
            case .pins:
                value = configuration.maxPinnedChatCount
            case .publicLinks:
                value = configuration.maxPublicLinksCount
            case .savedGifs:
                value = configuration.maxSavedGifCount
            case .favedStickers:
                value = configuration.maxFavedStickerCount
            case .about:
                value = configuration.maxAboutLength
            case .captions:
                value = configuration.maxCaptionLength
            case .folders:
                value = configuration.maxFoldersCount
            case .chatsPerFolder:
                value = configuration.maxFolderChatsCount
            case .account:
                value = isPremium ? 4 : 3
            case .recommendedChannels:
                value = configuration.maxChannelRecommendationsCount
        }
        return "\(value)"
    }
}

private final class LimitsListComponent: CombinedComponent {
    typealias EnvironmentType = (Empty, ScrollChildEnvironment)
    
    let context: AccountContext
    let theme: PresentationTheme
    let topInset: CGFloat
    let bottomInset: CGFloat
    
    init(context: AccountContext, theme: PresentationTheme, topInset: CGFloat, bottomInset: CGFloat) {
        self.context = context
        self.theme = theme
        self.topInset = topInset
        self.bottomInset = bottomInset
    }
    
    static func ==(lhs: LimitsListComponent, rhs: LimitsListComponent) -> Bool {
        if lhs.context !== rhs.context {
            return false
        }
        if lhs.theme !== rhs.theme {
            return false
        }
        if lhs.topInset != rhs.topInset {
            return false
        }
        if lhs.bottomInset != rhs.bottomInset {
            return false
        }
        return true
    }
    
    final class State: ComponentState {
        private let context: AccountContext
        
        private var disposable: Disposable?
        var limits: EngineConfiguration.UserLimits = .defaultValue
        var premiumLimits: EngineConfiguration.UserLimits = .defaultValue
        
        init(context: AccountContext) {
            self.context = context
          
            super.init()
            
            self.disposable = (context.engine.data.get(
                TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false),
                TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true)
            )
            |> deliverOnMainQueue).start(next: { [weak self] limits, premiumLimits in
                if let strongSelf = self {
                    strongSelf.limits = limits
                    strongSelf.premiumLimits = premiumLimits
                    strongSelf.updated(transition: .immediate)
                }
            })
        }
        
        deinit {
            self.disposable?.dispose()
        }
    }
    
    func makeState() -> State {
        return State(context: self.context)
    }
    
    static var body: Body {
        let list = Child(List<Empty>.self)
        
        return { context in
            let state = context.state
            let theme = context.component.context.sharedContext.currentPresentationData.with { $0 }.theme
            let strings = context.component.context.sharedContext.currentPresentationData.with { $0 }.strings
            
            let colors = [
                UIColor(rgb: 0xdf7138),
                UIColor(rgb: 0xd6593f),
                UIColor(rgb: 0xca4550),
                UIColor(rgb: 0xbc496d),
                UIColor(rgb: 0xae4c92),
                UIColor(rgb: 0x9153e5),
                UIColor(rgb: 0x825af6),
                UIColor(rgb: 0x676bf7),
                UIColor(rgb: 0x5991f8),
                UIColor(rgb: 0x5b99d0),
                UIColor(rgb: 0x5ea4a4)
            ]
            
            let items: [AnyComponentWithIdentity<Empty>] = Limit.allCases.enumerated().map { index, value in
                AnyComponentWithIdentity(
                    id: value, component: AnyComponent(
                        LimitComponent(
                            title: value.title(strings: strings),
                            titleColor: theme.list.itemPrimaryTextColor,
                            text: value.text(strings: strings),
                            textColor: theme.list.itemSecondaryTextColor,
                            accentColor: theme.list.itemAccentColor,
                            inactiveColor: theme.list.itemBlocksSeparatorColor.withAlphaComponent(0.5),
                            inactiveTextColor: theme.list.itemPrimaryTextColor,
                            inactiveTitle: strings.Premium_Free,
                            inactiveValue: value.limit(state.limits, isPremium: false),
                            activeColor: colors[index],
                            activeTextColor: .white,
                            activeTitle: strings.Premium_Premium,
                            activeValue: value.limit(state.premiumLimits, isPremium: true)
                        )
                    )
                )
            }
                                
            let list = list.update(
                component: List(items),
                availableSize: CGSize(width: context.availableSize.width, height: 10000.0),
                transition: context.transition
            )
            
            let contentHeight = context.component.topInset + list.size.height + context.component.bottomInset
            context.add(list
                .position(CGPoint(x: list.size.width / 2.0, y: context.component.topInset + list.size.height / 2.0))
            )
            
            return CGSize(width: context.availableSize.width, height: contentHeight)
        }
    }
}


final class LimitsPageComponent: CombinedComponent {
    typealias EnvironmentType = DemoPageEnvironment
    
    let context: AccountContext
    let theme: PresentationTheme
    let neighbors: PageNeighbors
    let bottomInset: CGFloat
    let updatedBottomAlpha: (CGFloat) -> Void
    let updatedDismissOffset: (CGFloat) -> Void
    let updatedIsDisplaying: (Bool) -> Void

    init(context: AccountContext, theme: PresentationTheme, neighbors: PageNeighbors, bottomInset: CGFloat, updatedBottomAlpha: @escaping (CGFloat) -> Void, updatedDismissOffset: @escaping (CGFloat) -> Void, updatedIsDisplaying: @escaping (Bool) -> Void) {
        self.context = context
        self.theme = theme
        self.neighbors = neighbors
        self.bottomInset = bottomInset
        self.updatedBottomAlpha = updatedBottomAlpha
        self.updatedDismissOffset = updatedDismissOffset
        self.updatedIsDisplaying = updatedIsDisplaying
    }
    
    static func ==(lhs: LimitsPageComponent, rhs: LimitsPageComponent) -> Bool {
        if lhs.context !== rhs.context {
            return false
        }
        if lhs.theme !== rhs.theme {
            return false
        }
        if lhs.neighbors != rhs.neighbors {
            return false
        }
        if lhs.bottomInset != rhs.bottomInset {
            return false
        }
        return true
    }
    
    final class State: ComponentState {
        let updateBottomAlpha: (CGFloat) -> Void
        let updateDismissOffset: (CGFloat) -> Void
        let updatedIsDisplaying: (Bool) -> Void
        
        var resetScroll: ActionSlot<CGPoint?>?
        
        var topContentOffset: CGFloat = 0.0
        var bottomContentOffset: CGFloat = 100.0 {
            didSet {
                self.updateAlpha()
            }
        }
        
        var position: CGFloat? {
            didSet {
                self.updateAlpha()
            }
        }
        
        var isDisplaying = false {
            didSet {
                if oldValue != self.isDisplaying {
                    self.updatedIsDisplaying(self.isDisplaying)
                    
                    if !self.isDisplaying {
                        self.resetScroll?.invoke(nil)
                    }
                }
            }
        }
        
        var neighbors = PageNeighbors(leftIsList: false, rightIsList: false)
        
        init(updateBottomAlpha: @escaping (CGFloat) -> Void, updateDismissOffset: @escaping (CGFloat) -> Void, updateIsDisplaying: @escaping (Bool) -> Void) {
            self.updateBottomAlpha = updateBottomAlpha
            self.updateDismissOffset = updateDismissOffset
            self.updatedIsDisplaying = updateIsDisplaying
            
            super.init()
        }
        
        func updateAlpha() {
            var dismissToLeft = false
            if let position = self.position, position > 0.0 {
                dismissToLeft = true
            }
            var dismissPosition = min(1.0, abs(self.position ?? 0.0) / 1.3333)
            var position = min(1.0, abs(self.position ?? 0.0))
            if position > 0.001, (dismissToLeft && self.neighbors.leftIsList) || (!dismissToLeft && self.neighbors.rightIsList) {
                dismissPosition = 0.0
                position = 0.0
            }
            self.updateDismissOffset(dismissPosition)
            
            let verticalPosition = 1.0 - min(30.0, self.bottomContentOffset) / 30.0
            
            let backgroundAlpha: CGFloat = max(position, verticalPosition)
            self.updateBottomAlpha(backgroundAlpha)
        }
    }
    
    func makeState() -> State {
        return State(updateBottomAlpha: self.updatedBottomAlpha, updateDismissOffset: self.updatedDismissOffset, updateIsDisplaying: self.updatedIsDisplaying)
    }
        
    static var body: Body {
        let background = Child(Rectangle.self)
        let scroll = Child(ScrollComponent<Empty>.self)
        let topPanel = Child(BlurredBackgroundComponent.self)
        let topSeparator = Child(Rectangle.self)
        let title = Child(MultilineTextComponent.self)
        
        let resetScroll = ActionSlot<CGPoint?>()
        
        return { context in
            let state = context.state
            
            let environment = context.environment[DemoPageEnvironment.self].value
            state.neighbors = context.component.neighbors
            state.resetScroll = resetScroll
            state.position = environment.position
            state.isDisplaying = environment.isDisplaying
            
            let theme = context.component.theme
            let strings = context.component.context.sharedContext.currentPresentationData.with { $0 }.strings
            
            let topInset: CGFloat = 56.0
            
            let scroll = scroll.update(
                component: ScrollComponent<Empty>(
                    content: AnyComponent(
                        LimitsListComponent(
                            context: context.component.context,
                            theme: context.component.theme,
                            topInset: topInset,
                            bottomInset: context.component.bottomInset
                        )
                    ),
                    contentInsets: UIEdgeInsets(top: topInset, left: 0.0, bottom: 0.0, right: 0.0),
                    contentOffsetUpdated: { [weak state] topContentOffset, bottomContentOffset in
                        state?.topContentOffset = topContentOffset
                        state?.bottomContentOffset = bottomContentOffset
                        Queue.mainQueue().justDispatch {
                            state?.updated(transition: .immediate)
                        }
                    },
                    contentOffsetWillCommit: { _ in },
                    resetScroll: resetScroll
                ),
                availableSize: context.availableSize,
                transition: context.transition
            )
                        
            let background = background.update(
                component: Rectangle(color: theme.overallDarkAppearance ? theme.list.blocksBackgroundColor : theme.list.plainBackgroundColor),
                availableSize: scroll.size,
                transition: context.transition
            )
            context.add(background
                .position(CGPoint(x: context.availableSize.width / 2.0, y: background.size.height / 2.0))
            )
     
            context.add(scroll
                .position(CGPoint(x: context.availableSize.width / 2.0, y: scroll.size.height / 2.0))
            )
            
            let topPanel = topPanel.update(
                component: BlurredBackgroundComponent(
                    color: theme.rootController.navigationBar.blurredBackgroundColor
                ),
                availableSize: CGSize(width: context.availableSize.width, height: topInset),
                transition: context.transition
            )
            
            let topSeparator = topSeparator.update(
                component: Rectangle(
                    color: theme.rootController.navigationBar.separatorColor
                ),
                availableSize: CGSize(width: context.availableSize.width, height: UIScreenPixel),
                transition: context.transition
            )
            
            let title = title.update(
                component: MultilineTextComponent(
                    text: .plain(NSAttributedString(string: strings.Premium_DoubledLimits, font: Font.semibold(17.0), textColor: theme.rootController.navigationBar.primaryTextColor)),
                    horizontalAlignment: .center,
                    truncationType: .end,
                    maximumNumberOfLines: 1
                ),
                availableSize: context.availableSize,
                transition: context.transition
            )
                  
            let topPanelAlpha: CGFloat = min(30.0, state.topContentOffset) / 30.0
            context.add(topPanel
                .position(CGPoint(x: context.availableSize.width / 2.0, y: topPanel.size.height / 2.0))
                .opacity(topPanelAlpha)
            )
            context.add(topSeparator
                .position(CGPoint(x: context.availableSize.width / 2.0, y: topPanel.size.height))
                .opacity(topPanelAlpha)
            )
            context.add(title
                .position(CGPoint(x: context.availableSize.width / 2.0, y: topPanel.size.height / 2.0))
            )
                        
            return scroll.size
        }
    }
}