import Foundation
import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
import TelegramCore
import TelegramPresentationData
import AccountContext
import TelegramPermissions
import AppBundle

public struct PermissionControllerCustomIcon: Equatable {
    let light: UIImage?
    let dark: UIImage?
    
    public init(light: UIImage?, dark: UIImage?) {
        self.light = light
        self.dark = dark
    }
}

public enum PermissionControllerContent: Equatable {
    case permission(PermissionState?)
    case custom(icon: PermissionContentIcon, title: String, subtitle: String?, text: String, buttonTitle: String, secondaryButtonTitle: String?, footerText: String?)
}

private struct PermissionControllerDataState: Equatable {
    var state: PermissionControllerContent?
}

private struct PermissionControllerLayoutState: Equatable {
    let layout: ContainerViewLayout
    let navigationHeight: CGFloat
}

private struct PermissionControllerInnerState: Equatable {
    var layout: PermissionControllerLayoutState?
    var data: PermissionControllerDataState
}

private struct PermissionControllerState: Equatable {
    var layout: PermissionControllerLayoutState
    var data: PermissionControllerDataState
}

extension PermissionControllerState {
    init?(_ state: PermissionControllerInnerState) {
        guard let layout = state.layout else {
            return nil
        }
        self.init(layout: layout, data: state.data)
    }
}

private func localizedString(for key: String, strings: PresentationStrings, fallback: String = "") -> String {
    if let string = strings.primaryComponent.dict[key] {
        return string
    } else if let string = strings.secondaryComponent?.dict[key] {
        return string
    } else {
        return fallback
    }
}

final class PermissionControllerNode: ASDisplayNode {
    private let context: AccountContext
    private var presentationData: PresentationData
    private let splitTest: PermissionUISplitTest?
    
    private var innerState: PermissionControllerInnerState
    
    private var contentNode: PermissionContentNode?
    
    var allow: (() -> Void)?
    var openPrivacyPolicy: (() -> Void)?
    var dismiss: (() -> Void)?
    
    init(context: AccountContext, splitTest: PermissionUISplitTest?) {
        self.context = context
        self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
        self.splitTest = splitTest
        self.innerState = PermissionControllerInnerState(layout: nil, data: PermissionControllerDataState(state: nil))
        
        super.init()
        
        self.setViewBlock({
            return UITracingLayerView()
        })
        
        self.updatePresentationData(self.presentationData)
    }
    
    func updatePresentationData(_ presentationData: PresentationData) {
        self.presentationData = presentationData
        
        self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
        self.contentNode?.updatePresentationData(self.presentationData)
    }
    
    func animateIn(completion: (() -> Void)? = nil) {
        self.layer.animatePosition(from: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), to: self.layer.position, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
    }
    
    func animateOut(completion: (() -> Void)? = nil) {
        self.layer.animatePosition(from: self.layer.position, to: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, completion: { _ in
            completion?()
        })
    }
    
    public func setState(_ state: PermissionControllerContent, transition: ContainedViewLayoutTransition) {
        self.updateState({ currentState -> PermissionControllerInnerState in
            return PermissionControllerInnerState(layout: currentState.layout, data: PermissionControllerDataState(state: state))
        }, transition: transition)
    }
    
    private func updateState(_ f: (PermissionControllerInnerState) -> PermissionControllerInnerState, transition: ContainedViewLayoutTransition) {
        let updatedState = f(self.innerState)
        if updatedState != self.innerState {
            self.innerState = updatedState
            if let state = PermissionControllerState(updatedState) {
                self.transition(state: state, transition: transition)
            }
        }
    }
    
    private func transition(state: PermissionControllerState, transition: ContainedViewLayoutTransition) {
        let insets = state.layout.layout.insets(options: [.statusBar])
        let contentFrame = CGRect(origin: CGPoint(x: 0.0, y: state.layout.navigationHeight), size: CGSize(width: state.layout.layout.size.width, height: state.layout.layout.size.height))
        
        if let state = state.data.state {
            switch state {
                case let .permission(permission):
                    if permission?.kind.rawValue != self.contentNode?.kind {
                        if let dataState = permission {
                            let icon: UIImage?
                            let title: String
                            let text: String
                            let buttonTitle: String
                            let hasPrivacyPolicy: Bool
                            
                            switch dataState {
                                case let .contacts(status):
                                    icon = UIImage(bundleImageName: "Settings/Permissions/Contacts")
                                    if let splitTest = self.splitTest, case let .modal(titleKey, textKey, allowTitleKey, allowInSettingsTitleKey) = splitTest.configuration.contacts {
                                        title = localizedString(for: titleKey, strings: self.presentationData.strings)
                                        text = localizedString(for: textKey, strings: self.presentationData.strings)
                                        if status == .denied {
                                            buttonTitle = localizedString(for: allowInSettingsTitleKey, strings: self.presentationData.strings)
                                        } else {
                                            buttonTitle = localizedString(for: allowTitleKey, strings: self.presentationData.strings)
                                        }
                                    } else {
                                        title = self.presentationData.strings.Permissions_ContactsTitle_v0
                                        text = self.presentationData.strings.Permissions_ContactsText_v0
                                        if status == .denied {
                                            buttonTitle = self.presentationData.strings.Permissions_ContactsAllowInSettings_v0
                                        } else {
                                            buttonTitle = self.presentationData.strings.Permissions_ContactsAllow_v0
                                        }
                                    }
                                    hasPrivacyPolicy = true
                                case let .notifications(status):
                                    icon = UIImage(bundleImageName: "Settings/Permissions/Notifications")
                                    if let splitTest = self.splitTest, case let .modal(titleKey, textKey, allowTitleKey, allowInSettingsTitleKey) = splitTest.configuration.notifications {
                                        title = localizedString(for: titleKey, strings: self.presentationData.strings, fallback: self.presentationData.strings.Permissions_NotificationsTitle_v0)
                                        text = localizedString(for: textKey, strings: self.presentationData.strings, fallback: self.presentationData.strings.Permissions_NotificationsText_v0)
                                        if status == .denied {
                                            buttonTitle = localizedString(for: allowInSettingsTitleKey, strings: self.presentationData.strings, fallback: self.presentationData.strings.Permissions_NotificationsAllowInSettings_v0)
                                        } else {
                                            buttonTitle = localizedString(for: allowTitleKey, strings: self.presentationData.strings, fallback: self.presentationData.strings.Permissions_NotificationsAllow_v0)
                                        }
                                    } else {
                                        title = self.presentationData.strings.Permissions_NotificationsTitle_v0
                                        text = self.presentationData.strings.Permissions_NotificationsText_v0
                                        if status == .denied {
                                            buttonTitle = self.presentationData.strings.Permissions_NotificationsAllowInSettings_v0
                                        } else {
                                            buttonTitle = self.presentationData.strings.Permissions_NotificationsAllow_v0
                                        }
                                    }
                                    hasPrivacyPolicy = false
                                case let .siri(status):
                                    icon = UIImage(bundleImageName: "Settings/Permissions/Siri")
                                    title = self.presentationData.strings.Permissions_SiriTitle_v0
                                    text = self.presentationData.strings.Permissions_SiriText_v0
                                    if status == .denied {
                                        buttonTitle = self.presentationData.strings.Permissions_SiriAllowInSettings_v0
                                    } else {
                                        buttonTitle = self.presentationData.strings.Permissions_SiriAllow_v0
                                    }
                                    hasPrivacyPolicy = false
                                case .cellularData:
                                    icon = UIImage(bundleImageName: "Settings/Permissions/CellularData")
                                    title = self.presentationData.strings.Permissions_CellularDataTitle_v0
                                    text = self.presentationData.strings.Permissions_CellularDataText_v0
                                    buttonTitle = self.presentationData.strings.Permissions_CellularDataAllowInSettings_v0
                                    hasPrivacyPolicy = false
                                case let .nearbyLocation(status):
                                    icon = nil
                                    title = self.presentationData.strings.Permissions_PeopleNearbyTitle_v0
                                    text = self.presentationData.strings.Permissions_PeopleNearbyText_v0
                                    if status == .denied {
                                        buttonTitle = self.presentationData.strings.Permissions_PeopleNearbyAllowInSettings_v0
                                    } else {
                                        buttonTitle = self.presentationData.strings.Permissions_PeopleNearbyAllow_v0
                                    }
                                    hasPrivacyPolicy = false
                            }
                            
                            let contentNode = PermissionContentNode(context: self.context, theme: self.presentationData.theme, strings: self.presentationData.strings, kind: dataState.kind.rawValue, icon: .image(icon), title: title, text: text, buttonTitle: buttonTitle, secondaryButtonTitle: nil, buttonAction: { [weak self] in
                                self?.allow?()
                                }, openPrivacyPolicy: hasPrivacyPolicy ? self.openPrivacyPolicy : nil)
                            self.insertSubnode(contentNode, at: 0)
                            contentNode.updateLayout(size: contentFrame.size, insets: insets, transition: .immediate)
                            contentNode.frame = contentFrame
                            if let currentContentNode = self.contentNode {
                                transition.updatePosition(node: currentContentNode, position: CGPoint(x: -contentFrame.size.width / 2.0, y: contentFrame.midY), completion: { [weak currentContentNode] _ in
                                    currentContentNode?.removeFromSupernode()
                                })
                                transition.animateHorizontalOffsetAdditive(node: contentNode, offset: -contentFrame.width)
                            } else if transition.isAnimated {
                                contentNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
                            }
                            self.contentNode = contentNode
                        } else if let currentContentNode = self.contentNode {
                            transition.updateAlpha(node: currentContentNode, alpha: 0.0, completion: { [weak currentContentNode] _ in
                                currentContentNode?.removeFromSupernode()
                            })
                            self.contentNode = nil
                        }
                    } else if let contentNode = self.contentNode {
                        transition.updateFrame(node: contentNode, frame: contentFrame)
                        contentNode.updateLayout(size: contentFrame.size, insets: insets, transition: transition)
                    }
                case let .custom(icon, title, subtitle, text, buttonTitle, secondaryButtonTitle, footerText):
                    if let contentNode = self.contentNode {
                        transition.updateFrame(node: contentNode, frame: contentFrame)
                        contentNode.updateLayout(size: contentFrame.size, insets: insets, transition: transition)
                    } else {                        
                        let contentNode = PermissionContentNode(context: self.context, theme: self.presentationData.theme, strings: self.presentationData.strings, kind: 0, icon: icon, title: title, subtitle: subtitle, text: text, buttonTitle: buttonTitle, secondaryButtonTitle: secondaryButtonTitle, footerText: footerText, buttonAction: { [weak self] in
                            self?.allow?()
                        }, openPrivacyPolicy: secondaryButtonTitle != nil ? { [weak self] in
                            self?.dismiss?()
                        } : nil)
                        self.insertSubnode(contentNode, at: 0)
                        contentNode.updateLayout(size: contentFrame.size, insets: insets, transition: .immediate)
                        contentNode.frame = contentFrame
                        self.contentNode = contentNode
                    }
            }
        }
    }
    
    func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
        self.updateState({ state in
            var state = state
            state.layout = PermissionControllerLayoutState(layout: layout, navigationHeight: navigationBarHeight)
            return state
        }, transition: transition)
    }
    
    @objc func privacyPolicyPressed() {
        self.openPrivacyPolicy?()
    }
}