import Foundation
import UIKit
import Display
import SwiftSignalKit
import TelegramCore
import LegacyComponents
import TelegramPresentationData
import ItemListUI
import PresentationDataUtils
import OverlayStatusController
import AccountContext
import AlertUI
import PresentationDataUtils
import UrlHandling
import AccountUtils
import PremiumUI
import StorageUsageScreen

private struct LogoutOptionsItemArguments {
    let addAccount: () -> Void
    let setPasscode: () -> Void
    let clearCache: () -> Void
    let changePhoneNumber: () -> Void
    let contactSupport: () -> Void
    let logout: () -> Void
}

private enum LogoutOptionsSection: Int32 {
    case options
    case logOut
}

private enum LogoutOptionsEntry: ItemListNodeEntry, Equatable {
    case alternativeHeader(PresentationTheme, String)
    case addAccount(PresentationTheme, String, String)
    case setPasscode(PresentationTheme, String, String)
    case clearCache(PresentationTheme, String, String)
    case changePhoneNumber(PresentationTheme, String, String)
    case contactSupport(PresentationTheme, String, String)
    case logout(PresentationTheme, String)
    case logoutInfo(PresentationTheme, String)
    
    var section: ItemListSectionId {
        switch self {
            case .alternativeHeader, .addAccount, .setPasscode, .clearCache, .changePhoneNumber, .contactSupport:
                return LogoutOptionsSection.options.rawValue
            case .logout, .logoutInfo:
                return LogoutOptionsSection.logOut.rawValue
        }
    }
    
    var stableId: Int32 {
        switch self {
            case .alternativeHeader:
                return 0
            case .addAccount:
                return 1
            case .setPasscode:
                return 2
            case .clearCache:
                return 3
            case .changePhoneNumber:
                return 4
            case .contactSupport:
                return 5
            case .logout:
                return 6
            case .logoutInfo:
                return 7
        }
    }
    
    static func <(lhs: LogoutOptionsEntry, rhs: LogoutOptionsEntry) -> Bool {
        return lhs.stableId < rhs.stableId
    }
    
    func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
        let arguments = arguments as! LogoutOptionsItemArguments
        switch self {
            case let .alternativeHeader(_, title):
                return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section)
            case let .addAccount(_, title, text):
                return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.addAccount, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
                    arguments.addAccount()
                })
            case let .setPasscode(_, title, text):
                return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.setPasscode, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
                    arguments.setPasscode()
                })
            case let .clearCache(_, title, text):
                return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.clearCache, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
                    arguments.clearCache()
                })
            case let .changePhoneNumber(_, title, text):
                return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.changePhoneNumber, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
                    arguments.changePhoneNumber()
                })
            case let .contactSupport(_, title, text):
                return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.support, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
                    arguments.contactSupport()
                })
            case let .logout(_, title):
                return ItemListActionItem(presentationData: presentationData, title: title, kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: {
                    arguments.logout()
                })
            case let .logoutInfo(_, title):
                return ItemListTextItem(presentationData: presentationData, text: .plain(title), sectionId: self.section)
        }
    }
}

private func logoutOptionsEntries(presentationData: PresentationData, canAddAccounts: Bool, hasPasscode: Bool) -> [LogoutOptionsEntry] {
    var entries: [LogoutOptionsEntry] = []
    entries.append(.alternativeHeader(presentationData.theme, presentationData.strings.LogoutOptions_AlternativeOptionsSection))
    if canAddAccounts {
        entries.append(.addAccount(presentationData.theme, presentationData.strings.LogoutOptions_AddAccountTitle, presentationData.strings.LogoutOptions_AddAccountText))
    }
    if !hasPasscode {
        entries.append(.setPasscode(presentationData.theme, presentationData.strings.LogoutOptions_SetPasscodeTitle, presentationData.strings.LogoutOptions_SetPasscodeText))
    }
    entries.append(.clearCache(presentationData.theme, presentationData.strings.LogoutOptions_ClearCacheTitle, presentationData.strings.LogoutOptions_ClearCacheText))
    entries.append(.changePhoneNumber(presentationData.theme, presentationData.strings.LogoutOptions_ChangePhoneNumberTitle, presentationData.strings.LogoutOptions_ChangePhoneNumberText))
    entries.append(.contactSupport(presentationData.theme, presentationData.strings.LogoutOptions_ContactSupportTitle, presentationData.strings.LogoutOptions_ContactSupportText))
    entries.append(.logout(presentationData.theme, presentationData.strings.LogoutOptions_LogOut))
    entries.append(.logoutInfo(presentationData.theme, presentationData.strings.LogoutOptions_LogOutInfo))
    return entries
}

public func logoutOptionsController(context: AccountContext, navigationController: NavigationController, canAddAccounts: Bool, phoneNumber: String) -> ViewController {
    var pushControllerImpl: ((ViewController) -> Void)?
    var presentControllerImpl: ((ViewController, Any?) -> Void)?
    var replaceTopControllerImpl: ((ViewController) -> Void)?
    var dismissImpl: (() -> Void)?
    
    let supportPeerDisposable = MetaDisposable()
    
    let arguments = LogoutOptionsItemArguments(addAccount: {
        let _ = (activeAccountsAndPeers(context: context)
        |> take(1)
        |> deliverOnMainQueue
        ).start(next: { accountAndPeer, accountsAndPeers in
            var maximumAvailableAccounts: Int = 3
            if accountAndPeer?.1.isPremium == true && !context.account.testingEnvironment {
                maximumAvailableAccounts = 4
            }
            var count: Int = 1
            for (accountContext, peer, _) in accountsAndPeers {
                if !accountContext.account.testingEnvironment {
                    if peer.isPremium {
                        maximumAvailableAccounts = 4
                    }
                    count += 1
                }
            }
            
            if count >= maximumAvailableAccounts {
                var replaceImpl: ((ViewController) -> Void)?
                let controller = PremiumLimitScreen(context: context, subject: .accounts, count: Int32(count), action: {
                    let controller = PremiumIntroScreen(context: context, source: .accounts)
                    replaceImpl?(controller)
                })
                replaceImpl = { [weak controller] c in
                    controller?.replace(with: c)
                }
                pushControllerImpl?(controller)
            } else {
                context.sharedContext.beginNewAuth(testingEnvironment: context.account.testingEnvironment)
                
                dismissImpl?()
            }
        })
    }, setPasscode: {
        let _ = passcodeOptionsAccessController(context: context, pushController: { controller in
            replaceTopControllerImpl?(controller)
        }, completion: { _ in
            replaceTopControllerImpl?(passcodeOptionsController(context: context))
        }).start(next: { controller in
            if let controller = controller {
                pushControllerImpl?(controller)
            }
        })
        dismissImpl?()
    }, clearCache: {
        pushControllerImpl?(StorageUsageScreen(context: context, makeStorageUsageExceptionsScreen: { category in
            return storageUsageExceptionsScreen(context: context, category: category)
        }))
        dismissImpl?()
    }, changePhoneNumber: {
        let introController = PrivacyIntroController(context: context, mode: .changePhoneNumber(phoneNumber), proceedAction: {
            replaceTopControllerImpl?(ChangePhoneNumberController(context: context))
        })
        pushControllerImpl?(introController)
        dismissImpl?()
    }, contactSupport: { [weak navigationController] in
        let supportPeer = Promise<EnginePeer.Id?>()
        supportPeer.set(context.engine.peers.supportPeerId())
        let presentationData = context.sharedContext.currentPresentationData.with { $0 }
        
        var faqUrl = presentationData.strings.Settings_FAQ_URL
        if faqUrl == "Settings.FAQ_URL" || faqUrl.isEmpty {
            faqUrl = "https://telegram.org/faq#general"
        }
        let resolvedUrl = resolveInstantViewUrl(account: context.account, url: faqUrl)
        
        let resolvedUrlPromise = Promise<ResolvedUrl>()
        resolvedUrlPromise.set(resolvedUrl)
        
        let openFaq: (Promise<ResolvedUrl>) -> Void = { resolvedUrl in
            let presentationData = context.sharedContext.currentPresentationData.with { $0 }
            let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
            presentControllerImpl?(controller, nil)
            let _ = (resolvedUrl.get()
            |> take(1)
            |> deliverOnMainQueue).start(next: { [weak controller] resolvedUrl in
                controller?.dismiss()
                dismissImpl?()
                
                context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { peer, navigation in
                }, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { controller, arguments in
                    pushControllerImpl?(controller)
                }, dismissInput: {}, contentContext: nil)
            })
        }
        
        presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Settings_FAQ_Intro, actions: [
            TextAlertAction(type: .genericAction, title: presentationData.strings.Settings_FAQ_Button, action: {
                openFaq(resolvedUrlPromise)
                dismissImpl?()
            }),
            TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
                supportPeerDisposable.set((supportPeer.get()
                |> take(1)
                |> deliverOnMainQueue).start(next: { peerId in
                    guard let peerId = peerId else {
                        return
                    }
                    let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
                    |> deliverOnMainQueue).start(next: { peer in
                        guard let peer = peer else {
                            return
                        }
                        if let navigationController = navigationController {
                            dismissImpl?()
                            context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer)))
                        }
                    })
                }))
            })
        ]), nil)
    }, logout: {
        let presentationData = context.sharedContext.currentPresentationData.with { $0 }
        let alertController = textAlertController(context: context, title: presentationData.strings.Settings_LogoutConfirmationTitle, text: presentationData.strings.Settings_LogoutConfirmationText, actions: [
            TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
            }),
            TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
                let _ = logoutFromAccount(id: context.account.id, accountManager: context.sharedContext.accountManager, alreadyLoggedOutRemotely: false).start()
                dismissImpl?()
            })
        ])
        presentControllerImpl?(alertController, nil)
    })
        
    let signal = combineLatest(queue: .mainQueue(),
        context.sharedContext.presentationData,
        context.sharedContext.accountManager.accessChallengeData()
    )
    |> map { presentationData, accessChallengeData -> (ItemListControllerState, (ItemListNodeState, Any)) in
        let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
            dismissImpl?()
        })
        
        var hasPasscode = false
        switch accessChallengeData.data {
            case .numericalPassword, .plaintextPassword:
                hasPasscode = true
            default:
                break
        }
        
        let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.LogoutOptions_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
        let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: logoutOptionsEntries(presentationData: presentationData, canAddAccounts: canAddAccounts, hasPasscode: hasPasscode), style: .blocks)
        
        return (controllerState, (listState, arguments))
    }
    
    let controller = ItemListController(context: context, state: signal, tabBarItem: nil)
    controller.navigationPresentation = .modal
    pushControllerImpl = { [weak navigationController] value in
        navigationController?.pushViewController(value, animated: false)
    }
    presentControllerImpl = { [weak controller] value, arguments in
        controller?.present(value, in: .window(.root), with: arguments ?? ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
    }
    replaceTopControllerImpl = { [weak navigationController] c in
        navigationController?.replaceTopController(c, animated: true)
    }
    dismissImpl = { [weak controller] in
        let _ = controller?.dismiss()
    }
    
    return controller
}