//
//  AppBannerView.swift
//  AltStore
//
//  Created by Riley Testut on 8/29/19.
//  Copyright © 2019 Riley Testut. All rights reserved.
//

import UIKit
import AltStoreCore
import Roxas

import Nuke

extension AppBannerView
{
    static let standardHeight = 88.0
    
    enum Style
    {
        case app
        case source
    }
    
    enum AppAction
    {
        case install
        case open
        case update
        case custom(String)
    }
}

class AppBannerView: RSTNibView
{
    override var accessibilityLabel: String? {
        get { return self.accessibilityView?.accessibilityLabel }
        set { self.accessibilityView?.accessibilityLabel = newValue }
    }
    
    override open var accessibilityAttributedLabel: NSAttributedString? {
        get { return self.accessibilityView?.accessibilityAttributedLabel }
        set { self.accessibilityView?.accessibilityAttributedLabel = newValue }
    }
    
    override var accessibilityValue: String? {
        get { return self.accessibilityView?.accessibilityValue }
        set { self.accessibilityView?.accessibilityValue = newValue }
    }
    
    override open var accessibilityAttributedValue: NSAttributedString? {
        get { return self.accessibilityView?.accessibilityAttributedValue }
        set { self.accessibilityView?.accessibilityAttributedValue = newValue }
    }
    
    override open var accessibilityTraits: UIAccessibilityTraits {
        get { return self.accessibilityView?.accessibilityTraits ?? [] }
        set { self.accessibilityView?.accessibilityTraits = newValue }
    }
    
    var style: Style = .app
    
    private var originalTintColor: UIColor?
    
    @IBOutlet var titleLabel: UILabel!
    @IBOutlet var subtitleLabel: UILabel!
    @IBOutlet var iconImageView: AppIconImageView!
    @IBOutlet var button: PillButton!
    @IBOutlet var buttonLabel: UILabel!
    @IBOutlet var betaBadgeView: UIView!
    @IBOutlet var sourceIconImageView: AppIconImageView!
    
    @IBOutlet var backgroundEffectView: UIVisualEffectView!
    
    @IBOutlet private var vibrancyView: UIVisualEffectView!
    @IBOutlet private var stackView: UIStackView!
    @IBOutlet private var accessibilityView: UIView!
    
    @IBOutlet private var iconImageViewHeightConstraint: NSLayoutConstraint!
    
    override init(frame: CGRect)
    {
        super.init(frame: frame)
        
        self.initialize()
    }
    
    required init?(coder: NSCoder)
    {
        super.init(coder: coder)
        
        self.initialize()
    }
    
    private func initialize()
    {
        self.accessibilityView.accessibilityTraits.formUnion(.button)
        
        self.isAccessibilityElement = false
        self.accessibilityElements = [self.accessibilityView, self.button].compactMap { $0 }
        
        self.betaBadgeView.isHidden = true
        
        self.sourceIconImageView.style = .circular
        self.sourceIconImageView.isHidden = true
        
        self.layoutMargins = self.stackView.layoutMargins
        self.insetsLayoutMarginsFromSafeArea = false
        
        self.stackView.isLayoutMarginsRelativeArrangement = true
        self.stackView.preservesSuperviewLayoutMargins = true
    }
    
    override func tintColorDidChange()
    {
        super.tintColorDidChange()
        
        if self.tintAdjustmentMode != .dimmed
        {
            self.originalTintColor = self.tintColor
        }
        
        self.update()
    }
}

extension AppBannerView
{
    func configure(for app: AppProtocol, action: AppAction? = nil, showSourceIcon: Bool = true)
    {
        struct AppValues
        {
            var name: String
            var developerName: String? = nil
            var isBeta: Bool = false
            
            init(app: AppProtocol)
            {
                self.name = app.name
                                
                guard let storeApp = (app as? StoreApp) ?? (app as? InstalledApp)?.storeApp else { return }
                self.developerName = storeApp.developerName

                if let track = storeApp.latestSupportedVersion?.channel,
                   ReleaseTracks.betaTracks.contains(track)
                {
                    self.name = String(format: NSLocalizedString("%@ beta", comment: ""), app.name)
                    self.isBeta = true
                }
            }
        }
        
        self.style = .app

        let values = AppValues(app: app)
        self.titleLabel.text = app.name // Don't use values.name since that already includes "beta".
        self.betaBadgeView.isHidden = !values.isBeta

        if let developerName = values.developerName
        {
            self.subtitleLabel.text = developerName
            self.accessibilityLabel = String(format: NSLocalizedString("%@ by %@", comment: ""), values.name, developerName)
        }
        else
        {
            self.subtitleLabel.text = NSLocalizedString("Sideloaded", comment: "")
            self.accessibilityLabel = values.name
        }
        self.buttonLabel.isHidden = true
        
        if let source = app.storeApp?.source, showSourceIcon
        {
            self.sourceIconImageView.isHidden = false
            self.sourceIconImageView.backgroundColor = source.effectiveTintColor?.adjustedForDisplay ?? .altPrimary
            
            if let iconURL = source.effectiveIconURL
            {
                if let image = ImageCache.shared[iconURL]
                {
                    self.sourceIconImageView.backgroundColor = .white
                    self.sourceIconImageView.image = image.image
                }
                else
                {
                    self.sourceIconImageView.image = nil
                    
                    Nuke.loadImage(with: iconURL, into: self.sourceIconImageView) { result in
                        switch result
                        {
                        case .failure(let error): Logger.main.error("Failed to fetch source icon from \(iconURL, privacy: .public). \(error.localizedDescription, privacy: .public)")
                        case .success: self.sourceIconImageView.backgroundColor = .white // In case icon has transparent background.
                        }
                    }
                }
            }
        }
        else
        {
            self.sourceIconImageView.isHidden = true
        }
        
        let buttonAction: AppAction
        
        if let action
        {
            buttonAction = action
        }
        else if let storeApp = app.storeApp
        {
            if let installedApp = storeApp.installedApp
            {
                // App is installed
                
                // if installedApp.isUpdateAvailable
                if installedApp.hasUpdate
                {
                    buttonAction = .update
                }
                else
                {
                    buttonAction = .open
                }
            }
            else
            {
                // App is not installed
                buttonAction = .install
            }
        }
        else
        {
            // App is not from a source, fall back to .open
            buttonAction = .open
        }
        
        UIView.performWithoutAnimation {
            switch buttonAction
            {
            case .open:
                let buttonTitle = NSLocalizedString("Open", comment: "")
                self.button.setTitle(buttonTitle.uppercased(), for: .normal)
                self.button.accessibilityLabel = String(format: NSLocalizedString("Open %@", comment: ""), values.name)
                self.button.accessibilityValue = buttonTitle
                
                self.button.countdownDate = nil
                
            case .update:
                let buttonTitle = NSLocalizedString("Update", comment: "")
                self.button.setTitle(buttonTitle.uppercased(), for: .normal)
                self.button.accessibilityLabel = String(format: NSLocalizedString("Update %@", comment: ""), values.name)
                self.button.accessibilityValue = buttonTitle
                
                self.button.countdownDate = nil
                
            case .custom(let buttonTitle):
                self.button.setTitle(buttonTitle, for: .normal)
                self.button.accessibilityLabel = buttonTitle
                self.button.accessibilityValue = buttonTitle
                
                self.button.countdownDate = nil
                
            case .install:
                if let storeApp = app.storeApp, storeApp.isPledgeRequired
                {
                    // Pledge required
                    
                    if storeApp.isPledged
                    {
                        let buttonTitle = NSLocalizedString("Install", comment: "")
                        self.button.setTitle(buttonTitle.uppercased(), for: .normal)
                        self.button.accessibilityLabel = String(format: NSLocalizedString("Install %@", comment: ""), app.name)
                        self.button.accessibilityValue = buttonTitle
                    }
                    else
                    {
                        let buttonTitle = NSLocalizedString("Pledge", comment: "")
                        self.button.setTitle(buttonTitle.uppercased(), for: .normal)
                        self.button.accessibilityLabel = buttonTitle
                        self.button.accessibilityValue = buttonTitle
                    }
                }
                else
                {
                    // Free app
                    
                    let buttonTitle = NSLocalizedString("Free", comment: "")
                    self.button.setTitle(buttonTitle.uppercased(), for: .normal)
                    self.button.accessibilityLabel = String(format: NSLocalizedString("Download %@", comment: ""), app.name)
                    self.button.accessibilityValue = buttonTitle
                }
                
                if let versionDate = app.storeApp?.latestSupportedVersion?.date, versionDate > Date()
                {
                    self.button.countdownDate = versionDate
                }
                else
                {
                    self.button.countdownDate = nil
                }
            }
            
            // Ensure PillButton is correct size before assigning progress.
            self.layoutIfNeeded()
        }
        
        if let progress = AppManager.shared.installationProgress(for: app), progress.fractionCompleted < 1.0
        {
            self.button.progress = progress
        }
        else
        {
            self.button.progress = nil
        }
    }
    
    func configure(for source: Source)
    {
        self.style = .source
        
        let subtitle: String
        if let text = source.subtitle
        {
            subtitle = text
        }
        else if let scheme = source.sourceURL.scheme
        {
            subtitle = source.sourceURL.absoluteString.replacingOccurrences(of: scheme + "://", with: "")
        }
        else
        {
            subtitle = source.sourceURL.absoluteString
        }
        
        self.titleLabel.text = source.name
        self.subtitleLabel.text = subtitle
        
        let tintColor = source.effectiveTintColor ?? .altPrimary
        self.tintColor = tintColor
        
        let accessibilityLabel = source.name + "\n" + subtitle
        self.accessibilityLabel = accessibilityLabel
    }
}

private extension AppBannerView
{
    func update()
    {
        self.clipsToBounds = true
        self.layer.cornerRadius = 22
        
        let tintColor = self.originalTintColor ?? self.tintColor
        self.subtitleLabel.textColor = tintColor
        
        switch self.style
        {
        case .app:
            self.directionalLayoutMargins.trailing = self.stackView.directionalLayoutMargins.trailing
            
            self.iconImageViewHeightConstraint.constant = 60
            self.iconImageView.style = .icon
            
            self.titleLabel.textColor = .label
            
            self.button.style = .pill
            
            self.backgroundEffectView.contentView.backgroundColor = UIColor(resource: .blurTint)
            self.backgroundEffectView.backgroundColor = tintColor
            
        case .source:
            self.directionalLayoutMargins.trailing = 20
            
            self.iconImageViewHeightConstraint.constant = 44
            self.iconImageView.style = .circular
            
            self.titleLabel.textColor = .white
            
            self.button.style = .custom
            
            self.backgroundEffectView.contentView.backgroundColor = tintColor?.adjustedForDisplay
            self.backgroundEffectView.backgroundColor = nil
            
            if let tintColor, tintColor.isTooBright
            {
                let textVibrancyEffect = UIVibrancyEffect(blurEffect: .init(style: .systemChromeMaterialLight), style: .fill)
                self.vibrancyView.effect = textVibrancyEffect
            }
            else
            {
                // Thinner == more dull
                let textVibrancyEffect = UIVibrancyEffect(blurEffect: .init(style: .systemThinMaterialDark), style: .secondaryLabel)
                self.vibrancyView.effect = textVibrancyEffect
            }
        }
    }
}
