#if ENABLE_WALLET

import Foundation
import UIKit
import Display
import WalletUI
import Postbox
import TelegramCore
import SyncCore
import AccountContext
import SwiftSignalKit
import TelegramPresentationData
import ShareController
import DeviceAccess
import PresentationDataUtils
import WalletCore

extension WalletConfiguration {
    static func with(appConfiguration: AppConfiguration) -> WalletConfiguration {
        if let data = appConfiguration.data, let config = data["wallet_config"] as? String, let blockchainName = data["wallet_blockchain_name"] as? String {
            var disableProxy = false
            if let value = data["wallet_disable_proxy"] as? String {
                disableProxy = value != "0"
            } else if let value = data["wallet_disable_proxy"] as? Int {
                disableProxy = value != 0
            }
            return WalletConfiguration(config: config, blockchainName: blockchainName, disableProxy: disableProxy)
        } else {
            return .defaultValue
        }
    }
}

final class WalletStorageInterfaceImpl: WalletStorageInterface {
    private let postbox: Postbox
    
    init(postbox: Postbox) {
        self.postbox = postbox
    }
    
    func watchWalletRecords() -> Signal<[WalletStateRecord], NoError> {
        return self.postbox.preferencesView(keys: [PreferencesKeys.walletCollection])
        |> map { view -> [WalletStateRecord] in
            guard let walletCollection = view.values[PreferencesKeys.walletCollection] as? WalletCollection else {
                return []
            }
            return walletCollection.wallets.flatMap { item -> WalletStateRecord? in
                do {
                    return WalletStateRecord(info: try JSONDecoder().decode(WalletInfo.self, from: item.info), exportCompleted: item.exportCompleted, state: item.state.flatMap { try? JSONDecoder().decode(CombinedWalletState.self, from: $0) })
                } catch {
                    return nil
                }
            }
        }
    }
    
    func getWalletRecords() -> Signal<[WalletStateRecord], NoError> {
        return self.postbox.transaction { transaction -> [WalletStateRecord] in
            guard let walletCollection = transaction.getPreferencesEntry(key: PreferencesKeys.walletCollection) as? WalletCollection else {
                return []
            }
            return walletCollection.wallets.flatMap { item -> WalletStateRecord? in
                do {
                    return WalletStateRecord(info: try JSONDecoder().decode(WalletInfo.self, from: item.info), exportCompleted: item.exportCompleted, state: item.state.flatMap { try? JSONDecoder().decode(CombinedWalletState.self, from: $0) })
                } catch {
                    return nil
                }
            }
        }
    }
    
    func updateWalletRecords(_ f: @escaping ([WalletStateRecord]) -> [WalletStateRecord]) -> Signal<[WalletStateRecord], NoError> {
        return self.postbox.transaction { transaction -> [WalletStateRecord] in
            var updatedRecords: [WalletStateRecord] = []
            transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
                var walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: [])
                let updatedItems = f(walletCollection.wallets.flatMap { item -> WalletStateRecord? in
                    do {
                        return WalletStateRecord(info: try JSONDecoder().decode(WalletInfo.self, from: item.info), exportCompleted: item.exportCompleted, state: item.state.flatMap { try? JSONDecoder().decode(CombinedWalletState.self, from: $0) })
                    } catch {
                        return nil
                    }
                })
                walletCollection.wallets = updatedItems.flatMap { item in
                    do {
                        return WalletCollectionItem(info: try JSONEncoder().encode(item.info), exportCompleted: item.exportCompleted, state: item.state.flatMap {
                            try? JSONEncoder().encode($0)
                        })
                    } catch {
                        return nil
                    }
                }
                return walletCollection
            })
            return updatedRecords
        }
    }
    
    func localWalletConfiguration() -> Signal<LocalWalletConfiguration, NoError> {
        return .single(LocalWalletConfiguration(source: .string(""), blockchainName: ""))
    }
    
    func updateLocalWalletConfiguration(_ f: @escaping (LocalWalletConfiguration) -> LocalWalletConfiguration) -> Signal<Never, NoError> {
        return .complete()
    }
}

final class WalletContextImpl: WalletContext {
    private let context: AccountContext
    
    let storage: WalletStorageInterface
    let tonInstance: TonInstance
    let keychain: TonKeychain
    let strings: PresentationStrings
    let presentationData: WalletPresentationData
    
    let supportsCustomConfigurations: Bool = false
    var termsUrl: String? {
        return self.strings.TelegramWallet_Intro_TermsUrl
    }
    var feeInfoUrl: String? {
        return self.strings.AppWallet_TransactionInfo_FeeInfoURL
    }
    
    var inForeground: Signal<Bool, NoError> {
        return self.context.sharedContext.applicationBindings.applicationInForeground
    }
    
    func downloadFile(url: URL) -> Signal<Data, WalletDownloadFileError> {
        return .fail(.generic)
    }
    
    func updateResolvedWalletConfiguration(source: LocalWalletConfigurationSource, blockchainName: String, resolvedValue: String) -> Signal<Never, NoError> {
        return .complete()
    }
    
    init(context: AccountContext, tonContext: TonContext) {
        self.context = context
        
        self.storage = WalletStorageInterfaceImpl(postbox: self.context.account.postbox)

        self.tonInstance = tonContext.instance
        self.keychain = tonContext.keychain
        let presentationData = context.sharedContext.currentPresentationData.with { $0 }
        self.strings = presentationData.strings
        let theme = presentationData.theme
        let strings = presentationData.strings
        let timeFormat: WalletTimeFormat
        switch presentationData.dateTimeFormat.timeFormat {
        case .military:
            timeFormat = .military
        case .regular:
            timeFormat = .regular
        }
        let dateFormat: WalletDateFormat
        switch presentationData.dateTimeFormat.dateFormat {
        case .dayFirst:
            dateFormat = .dayFirst
        case .monthFirst:
            dateFormat = .monthFirst
        }
        
        let navigationBarData = NavigationBarPresentationData(presentationData: presentationData)
        
        self.presentationData = WalletPresentationData(
            theme: WalletTheme(
                info: WalletInfoTheme(
                    buttonBackgroundColor: UIColor(rgb: 0x32aafe),
                    buttonTextColor: .white,
                    incomingFundsTitleColor: theme.chatList.secretTitleColor,
                    outgoingFundsTitleColor: theme.list.itemDestructiveColor
                ), transaction: WalletTransactionTheme(
                    descriptionBackgroundColor: theme.chat.message.incoming.bubble.withoutWallpaper.fill,
                    descriptionTextColor: theme.chat.message.incoming.primaryTextColor
                ), setup: WalletSetupTheme(
                    buttonFillColor: theme.list.itemCheckColors.fillColor,
                    buttonForegroundColor: theme.list.itemCheckColors.foregroundColor,
                    inputBackgroundColor: theme.actionSheet.inputBackgroundColor,
                    inputPlaceholderColor: theme.actionSheet.inputPlaceholderColor,
                    inputTextColor: theme.actionSheet.inputTextColor,
                    inputClearButtonColor: theme.actionSheet.inputClearButtonColor.withAlphaComponent(0.8)
                ),
                list: WalletListTheme(
                    itemPrimaryTextColor: theme.list.itemPrimaryTextColor,
                    itemSecondaryTextColor: theme.list.itemSecondaryTextColor,
                    itemPlaceholderTextColor: theme.list.itemPlaceholderTextColor,
                    itemDestructiveColor: theme.list.itemDestructiveColor,
                    itemAccentColor: theme.list.itemAccentColor,
                    itemDisabledTextColor: theme.list.itemDisabledTextColor,
                    plainBackgroundColor: theme.list.plainBackgroundColor,
                    blocksBackgroundColor: theme.list.blocksBackgroundColor,
                    itemPlainSeparatorColor: theme.list.itemPlainSeparatorColor,
                    itemBlocksBackgroundColor: theme.list.itemBlocksBackgroundColor,
                    itemBlocksSeparatorColor: theme.list.itemBlocksSeparatorColor,
                    itemHighlightedBackgroundColor: theme.list.itemHighlightedBackgroundColor,
                    sectionHeaderTextColor: theme.list.sectionHeaderTextColor,
                    freeTextColor: theme.list.freeTextColor,
                    freeTextErrorColor: theme.list.freeTextErrorColor,
                    inputClearButtonColor: theme.list.inputClearButtonColor
                ),
                statusBarStyle: theme.rootController.statusBarStyle.style,
                navigationBar: navigationBarData.theme,
                keyboardAppearance: theme.rootController.keyboardColor.keyboardAppearance,
                alert: AlertControllerTheme(presentationData: presentationData),
                actionSheet: ActionSheetControllerTheme(presentationData: presentationData)
            ), strings: WalletStrings(
                primaryComponent: WalletStringsComponent(
                    languageCode: strings.primaryComponent.languageCode,
                    localizedName: strings.primaryComponent.localizedName,
                    pluralizationRulesCode: strings.primaryComponent.pluralizationRulesCode,
                    dict: strings.primaryComponent.dict
                ),
                secondaryComponent: strings.secondaryComponent.flatMap { component in
                    return WalletStringsComponent(
                        languageCode: component.languageCode,
                        localizedName: component.localizedName,
                        pluralizationRulesCode: component.pluralizationRulesCode,
                        dict: component.dict
                    )
                },
                groupingSeparator: strings.groupingSeparator
            ), dateTimeFormat: WalletPresentationDateTimeFormat(
                timeFormat: timeFormat,
                dateFormat: dateFormat,
                dateSeparator: presentationData.dateTimeFormat.dateSeparator,
                decimalSeparator: presentationData.dateTimeFormat.decimalSeparator,
                groupingSeparator: presentationData.dateTimeFormat.groupingSeparator
            )
        )
    }
    
    func getServerSalt() -> Signal<Data, WalletContextGetServerSaltError> {
        return getServerWalletSalt(network: self.context.account.network)
        |> mapError { _ -> WalletContextGetServerSaltError in
            return .generic
        }
    }
    
    func presentNativeController(_ controller: UIViewController) {
        self.context.sharedContext.mainWindow?.presentNative(controller)
    }
    
    func idleTimerExtension() -> Disposable {
        return self.context.sharedContext.applicationBindings.pushIdleTimerExtension()
    }
    
    func openUrl(_ url: String) {
        return self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: true, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: {})
    }
    
    func shareUrl(_ url: String) {
        let controller = ShareController(context: self.context, subject: .url(url))
        self.context.sharedContext.mainWindow?.present(controller, on: .root)
    }
    
    func openPlatformSettings() {
        self.context.sharedContext.applicationBindings.openSettings()
    }
    
    func authorizeAccessToCamera(completion: @escaping () -> Void) {
        let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
        DeviceAccess.authorizeAccess(to: .camera(.video), presentationData: presentationData, present: { c, a in
            c.presentationArguments = a
            self.context.sharedContext.mainWindow?.present(c, on: .root)
        }, openSettings: { [weak self] in
            self?.openPlatformSettings()
        }, { granted in
            guard granted else {
                return
            }
            completion()
        })
    }
    
    func pickImage(present: @escaping (ViewController) -> Void, completion: @escaping (UIImage) -> Void) {
        self.context.sharedContext.openImagePicker(context: self.context, completion: { image in
            completion(image)
        }, present: { [weak self] controller in
            present(controller)
        })
    }
}

#endif