//
//  SuggestedProfileCardCollectionViewCell.swift
//  Tusker
//
//  Created by Shadowfacts on 1/23/23.
//  Copyright © 2023 Shadowfacts. All rights reserved.
//

import UIKit
import Pachyderm
import SwiftUI

class SuggestedProfileCardCollectionViewCell: UICollectionViewCell {
    
    weak var delegate: (any TuskerNavigationDelegate)?
    private var mastodonController: MastodonController! { delegate?.apiController }
    
    private var accountID: String!
    private var source: Suggestion.Source!
    
    @IBOutlet weak var headerImageView: CachedImageView!
    @IBOutlet weak var avatarContainerView: UIView!
    @IBOutlet weak var avatarImageView: CachedImageView!
    @IBOutlet weak var displayNameLabel: AccountDisplayNameLabel!
    @IBOutlet weak var usernameLabel: UILabel!
    @IBOutlet weak var noteTextView: StatusContentTextView!
    @IBOutlet weak var suggestionSourceButton: UIButton!
    
    private var hoverGestureAnimator: UIViewPropertyAnimator?
    
    override func awakeFromNib() {
        super.awakeFromNib()
        
        layer.shadowOpacity = 0.2
        layer.shadowRadius = 8
        layer.shadowOffset = .zero
        layer.masksToBounds = false
        contentView.layer.cornerRadius = 12.5
        contentView.layer.cornerCurve = .continuous
        contentView.backgroundColor = .appGroupedCellBackground
        updateLayerColors()
        
        headerImageView.cache = .headers
        
        avatarContainerView.layer.masksToBounds = true
        avatarContainerView.layer.cornerCurve = .continuous
        avatarImageView.cache = .avatars
        avatarImageView.layer.masksToBounds = true
        avatarImageView.layer.cornerCurve = .continuous
        
        displayNameLabel.font = UIFontMetrics(forTextStyle: .title1).scaledFont(for: .systemFont(ofSize: 24, weight: .semibold))
        displayNameLabel.adjustsFontForContentSizeCategory = true
        
        usernameLabel.font = UIFontMetrics.default.scaledFont(for: .systemFont(ofSize: 15, weight: .light))
        usernameLabel.adjustsFontForContentSizeCategory = true
        
        noteTextView.defaultFont = .preferredFont(forTextStyle: .body)
        noteTextView.monospaceFont = UIFontMetrics.default.scaledFont(for: .monospacedSystemFont(ofSize: 17, weight: .regular))
        noteTextView.adjustsFontForContentSizeCategory = true
        noteTextView.textContainer.lineBreakMode = .byTruncatingTail
        
        addGestureRecognizer(UIHoverGestureRecognizer(target: self, action: #selector(hoverRecognized)))
        
        NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
    }
    
    @objc private func preferencesChanged() {
        guard let accountID,
              let mastodonController,
              let account = mastodonController.persistentContainer.account(for: accountID) else {
            return
        }
        updateUIForPreferences(account: account)
    }
    
    func updateUI(accountID: String, source: Suggestion.Source) {
        guard self.accountID != accountID,
              let account = mastodonController.persistentContainer.account(for: accountID) else {
            return
        }
        self.accountID = accountID
        self.source = source
        
        updateUIForPreferences(account: account)
        
        avatarImageView.update(for: account.avatar)
        headerImageView.update(for: account.header)
        usernameLabel.text = "@\(account.acct)"
        noteTextView.setTextFromHtml(account.note)
        
        var config = UIButton.Configuration.plain()
        config.image = source.image
        suggestionSourceButton.configuration = config
        suggestionSourceButton.setNeedsUpdateConfiguration()
    }
    
    private func updateUIForPreferences(account: AccountMO) {
        avatarContainerView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarContainerView)
        avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView)
        displayNameLabel.updateForAccountDisplayName(account: account)
    }
    
    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        super.traitCollectionDidChange(previousTraitCollection)
        updateLayerColors()
    }
    
    private func updateLayerColors() {
        if traitCollection.userInterfaceStyle == .dark {
            layer.shadowColor = UIColor.darkGray.cgColor
        } else {
            layer.shadowColor = UIColor.black.cgColor
        }
    }
    
    // MARK: Interaction
    
    @IBAction func suggestionSourceButtonPressed(_ sender: Any) {
        guard let delegate,
              let source else {
            return
        }
        let view = SuggestionSourceView(mastodonController: mastodonController, source: source)
        let host = UIHostingController(rootView: view)
        let toPresent: UIViewController
        if traitCollection.horizontalSizeClass == .compact || traitCollection.verticalSizeClass == .compact {
            toPresent = UINavigationController(rootViewController: host)
            toPresent.modalPresentationStyle = .pageSheet
            let sheetPresentationController = toPresent.sheetPresentationController!
            sheetPresentationController.detents = [
                .medium()
            ]
        } else {
            host.modalPresentationStyle = .popover
            let popoverPresentationController = host.popoverPresentationController!
            popoverPresentationController.sourceView = suggestionSourceButton
            host.preferredContentSize = host.sizeThatFits(in: CGSize(width: 400, height: CGFloat.infinity))
            toPresent = host
        }
        delegate.present(toPresent, animated: true)
    }
    
    @objc private func hoverRecognized(_ recognizer: UIHoverGestureRecognizer) {
        switch recognizer.state {
        case .began, .changed:
            hoverGestureAnimator = UIViewPropertyAnimator(duration: 0.2, curve: .easeInOut, animations: {
                self.transform = CGAffineTransform(scaleX: 1.05, y: 1.05)
            })
            hoverGestureAnimator!.startAnimation()
        case .ended:
            hoverGestureAnimator?.stopAnimation(true)
            hoverGestureAnimator?.addAnimations {
                self.transform = .identity
            }
            hoverGestureAnimator?.startAnimation()
        default:
            break
        }
    }

}

private extension Suggestion.Source {
    var image: UIImage {
        switch self {
        case .global:
            return UIImage(systemName: "chart.line.uptrend.xyaxis")!
        case .pastInteractions:
            return UIImage(systemName: "clock.arrow.circlepath")!
        case .staff:
            return UIImage(systemName: "person.2")!
        }
    }
    
    var title: String {
        switch self {
        case .global:
            return "Popular Recently"
        case .pastInteractions:
            return "Past Interactions"
        case .staff:
            return "Staff Recommendation"
        }
    }
}

private struct SuggestionSourceView: View {
    let mastodonController: MastodonController
    let source: Suggestion.Source
    
    @Environment(\.dismiss) private var dismiss
    
    var body: some View {
        VStack(alignment: .leading) {
            HStack {
                Image(uiImage: source.image.withRenderingMode(.alwaysTemplate))
                Text(source.title)
                Spacer()
            }
            switch source {
            case .global:
                Text("This account is suggested for you because it has been highly active within the past 30 days.")
            case .pastInteractions:
                Text("This account is suggested for you because you have interacted with it before.")
            case .staff:
                Text("This account is recommended by the staff of \(mastodonController.accountInfo!.instanceURL.host!)")
            }
        }
        .padding()
        .navigationTitle("Suggestion Reason")
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            ToolbarItem(placement: .cancellationAction) {
                Button("Done") {
                    dismiss()
                }
            }
        }
    }
}
