import Foundation
import UIKit
import Display
import ComponentFlow
import MultilineTextComponent
import CheckNode
import Markdown

final class PremiumOptionComponent: CombinedComponent {
    let title: String
    let subtitle: String
    let labelPrice: String
    let discount: String
    let multiple: Bool
    let selected: Bool
    let primaryTextColor: UIColor
    let secondaryTextColor: UIColor
    let accentColor: UIColor
    let checkForegroundColor: UIColor
    let checkBorderColor: UIColor
    
    init(
        title: String,
        subtitle: String,
        labelPrice: String,
        discount: String,
        multiple: Bool = false,
        selected: Bool,
        primaryTextColor: UIColor,
        secondaryTextColor: UIColor,
        accentColor: UIColor,
        checkForegroundColor: UIColor,
        checkBorderColor: UIColor
    ) {
        self.title = title
        self.subtitle = subtitle
        self.labelPrice = labelPrice
        self.discount = discount
        self.multiple = multiple
        self.selected = selected
        self.primaryTextColor = primaryTextColor
        self.secondaryTextColor = secondaryTextColor
        self.accentColor = accentColor
        self.checkForegroundColor = checkForegroundColor
        self.checkBorderColor = checkBorderColor
    }
    
    static func ==(lhs: PremiumOptionComponent, rhs: PremiumOptionComponent) -> Bool {
        if lhs.title != rhs.title {
            return false
        }
        if lhs.subtitle != rhs.subtitle {
            return false
        }
        if lhs.labelPrice != rhs.labelPrice {
            return false
        }
        if lhs.discount != rhs.discount {
            return false
        }
        if lhs.multiple != rhs.multiple {
            return false
        }
        if lhs.selected != rhs.selected {
            return false
        }
        if lhs.primaryTextColor != rhs.primaryTextColor {
            return false
        }
        if lhs.secondaryTextColor != rhs.secondaryTextColor {
            return false
        }
        if lhs.accentColor != rhs.accentColor {
            return false
        }
        if lhs.checkForegroundColor != rhs.checkForegroundColor {
            return false
        }
        if lhs.checkBorderColor != rhs.checkBorderColor {
            return false
        }
        return true
    }
    
    static var body: Body {
        let check = Child(CheckComponent.self)
        let title = Child(MultilineTextComponent.self)
        let subtitle = Child(MultilineTextComponent.self)
        let discountBackground = Child(RoundedRectangle.self)
        let discount = Child(MultilineTextComponent.self)
        let label = Child(MultilineTextComponent.self)
        
        return { context in
            let component = context.component
            
            var insets = UIEdgeInsets(top: 11.0, left: 46.0, bottom: 13.0, right: 16.0)
                        
            let label = label.update(
                component: MultilineTextComponent(
                    text: .plain(
                        NSAttributedString(
                            string: component.labelPrice,
                            font: Font.regular(17),
                            textColor: component.secondaryTextColor
                        )
                    ),
                    maximumNumberOfLines: 1
                ),
                availableSize: context.availableSize,
                transition: context.transition
            )
            
            let title = title.update(
                component: MultilineTextComponent(
                    text: .plain(
                        NSAttributedString(
                            string: component.title,
                            font: Font.regular(17),
                            textColor: component.primaryTextColor
                        )
                    ),
                    maximumNumberOfLines: 1
                ),
                availableSize: CGSize(width: context.availableSize.width - insets.left - insets.right - label.size.width, height: context.availableSize.height),
                transition: context.transition
            )
                     
            let discountSize: CGSize
            if !component.discount.isEmpty {
                let discount = discount.update(
                    component: MultilineTextComponent(
                        text: .plain(
                            NSAttributedString(
                                string: component.discount,
                                font: Font.with(size: 14.0, design: .round, weight: .semibold, traits: []),
                                textColor: .white
                            )
                        ),
                        maximumNumberOfLines: 1
                    ),
                    availableSize: context.availableSize,
                    transition: context.transition
                )
                
                discountSize = CGSize(width: discount.size.width + 6.0, height: 18.0)
            
                let discountBackground = discountBackground.update(
                    component: RoundedRectangle(
                        color: component.accentColor,
                        cornerRadius: 5.0
                    ),
                    availableSize: discountSize,
                    transition: context.transition
                )
                
                let discountPosition = CGPoint(x: insets.left + title.size.width + 6.0 + discountSize.width / 2.0, y: insets.top + title.size.height / 2.0)
                
                context.add(discountBackground
                    .position(discountPosition)
                )
                context.add(discount
                    .position(discountPosition)
                )
            } else {
                discountSize = CGSize(width: 0.0, height: 18.0)
            }
                        
            var spacing: CGFloat = 0.0
            var subtitleSize = CGSize()
            if !component.subtitle.isEmpty {
                spacing = 2.0
                
                let subtitleFont = Font.regular(13)
                let subtitleColor = component.secondaryTextColor
                
                let subtitleString = parseMarkdownIntoAttributedString(
                    component.subtitle,
                    attributes: MarkdownAttributes(
                        body: MarkdownAttributeSet(font: subtitleFont, textColor: subtitleColor),
                        bold: MarkdownAttributeSet(font: subtitleFont, textColor: subtitleColor, additionalAttributes: [NSAttributedString.Key.strikethroughStyle.rawValue: NSUnderlineStyle.single.rawValue as NSNumber]),
                        link: MarkdownAttributeSet(font: subtitleFont, textColor: subtitleColor),
                        linkAttribute: { _ in return nil }
                    )
                )
                
                let subtitle = subtitle.update(
                    component: MultilineTextComponent(
                        text: .plain(subtitleString),
                        maximumNumberOfLines: 1
                    ),
                    availableSize: CGSize(width: context.availableSize.width - insets.left - insets.right, height: context.availableSize.height),
                    transition: context.transition
                )
                context.add(subtitle
                    .position(CGPoint(x: insets.left + subtitle.size.width / 2.0, y: insets.top + title.size.height + spacing + subtitle.size.height / 2.0))
                )
                subtitleSize = subtitle.size
                
                insets.top -= 2.0
                insets.bottom -= 2.0
            }
            
            let check = check.update(
                component: CheckComponent(
                    theme: CheckComponent.Theme(
                        backgroundColor: component.accentColor,
                        strokeColor: component.checkForegroundColor,
                        borderColor: component.checkBorderColor,
                        overlayBorder: false,
                        hasInset: false,
                        hasShadow: false
                    ),
                    selected: component.selected
                ),
                availableSize: context.availableSize,
                transition: context.transition
            )
                
            context.add(title
                .position(CGPoint(x: insets.left + title.size.width / 2.0, y: insets.top + title.size.height / 2.0))
            )
               
            let size = CGSize(width: context.availableSize.width, height: insets.top + title.size.height + spacing + subtitleSize.height + insets.bottom)
            
            let distance = context.availableSize.width - insets.left - insets.right - label.size.width - subtitleSize.width
            
            let labelY: CGFloat
            if distance > 8.0 {
                labelY = size.height / 2.0
            } else {
                labelY = insets.top + title.size.height / 2.0
            }
            
            context.add(label
                .position(CGPoint(x: context.availableSize.width - insets.right - label.size.width / 2.0, y: labelY))
            )
            
            context.add(check
                .position(CGPoint(x: 4.0 + check.size.width / 2.0, y: size.height / 2.0))
            )
            
            return size
        }
    }
}

private final class CheckComponent: Component {
    struct Theme: Equatable {
        public let backgroundColor: UIColor
        public let strokeColor: UIColor
        public let borderColor: UIColor
        public let overlayBorder: Bool
        public let hasInset: Bool
        public let hasShadow: Bool
        public let filledBorder: Bool
        public let borderWidth: CGFloat?
        
        public init(backgroundColor: UIColor, strokeColor: UIColor, borderColor: UIColor, overlayBorder: Bool, hasInset: Bool, hasShadow: Bool, filledBorder: Bool = false, borderWidth: CGFloat? = nil) {
            self.backgroundColor = backgroundColor
            self.strokeColor = strokeColor
            self.borderColor = borderColor
            self.overlayBorder = overlayBorder
            self.hasInset = hasInset
            self.hasShadow = hasShadow
            self.filledBorder = filledBorder
            self.borderWidth = borderWidth
        }
        
        var checkNodeTheme: CheckNodeTheme {
            return CheckNodeTheme(
                backgroundColor: self.backgroundColor,
                strokeColor: self.strokeColor,
                borderColor: self.borderColor,
                overlayBorder: self.overlayBorder,
                hasInset: self.hasInset,
                hasShadow: self.hasShadow,
                filledBorder: self.filledBorder,
                borderWidth: self.borderWidth
            )
        }
    }
    
    let theme: Theme
    let selected: Bool
    
    init(
        theme: Theme,
        selected: Bool
    ) {
        self.theme = theme
        self.selected = selected
    }
    
    static func ==(lhs: CheckComponent, rhs: CheckComponent) -> Bool {
        if lhs.theme != rhs.theme {
            return false
        }
        if lhs.selected != rhs.selected {
            return false
        }
        return true
    }
    
    final class View: UIView {
        private var currentValue: CGFloat?
        private var animator: DisplayLinkAnimator?

        private var checkLayer: CheckLayer {
            return self.layer as! CheckLayer
        }
        
        override class var layerClass: AnyClass {
            return CheckLayer.self
        }
        
        init() {
            super.init(frame: CGRect())
        }

        required init?(coder aDecoder: NSCoder) {
            preconditionFailure()
        }

    
        func update(component: CheckComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize {
            self.checkLayer.setSelected(component.selected, animated: true)
            self.checkLayer.theme = component.theme.checkNodeTheme
            
            return CGSize(width: 22.0, height: 22.0)
        }
    }

    func makeView() -> View {
        return View()
    }

    func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
        return view.update(component: self, availableSize: availableSize, transition: transition)
    }
}