// Copyright © 2020 Metabolist. All rights reserved.

import SDWebImage
import UIKit
import ViewModels

final class AccountHeaderView: UIView {
    let headerImageBackgroundView = UIView()
    let headerImageView = SDAnimatedImageView()
    let headerButton = UIButton()
    let avatarBackgroundView = UIView()
    let avatarImageView = SDAnimatedImageView()
    let avatarButton = UIButton()
    let relationshipButtonsStackView = UIStackView()
    let directMessageButton = UIButton()
    let followButton = UIButton(type: .system)
    let unfollowButton = UIButton(type: .system)
    let notifyButton = UIButton()
    let unnotifyButton = UIButton()
    let displayNameLabel = AnimatedAttachmentLabel()
    let accountStackView = UIStackView()
    let accountLabel = CopyableLabel()
    let lockedImageView = UIImageView()
    let followsYouLabel = CapsuleLabel()
    let mutedLabel = CapsuleLabel()
    let blockedLabel = CapsuleLabel()
    let statusCountJoinedStackView = UIStackView()
    let statusCountLabel = UILabel()
    let statusCountJoinedSeparatorLabel = UILabel()
    let joinedLabel = UILabel()
    let fieldsStackView = UIStackView()
    let noteTextView = TouchFallthroughTextView()
    let followStackView = UIStackView()
    let followingButton = UIButton()
    let followersButton = UIButton()
    let segmentedControl = UISegmentedControl()
    let unavailableLabel = UILabel()

    var viewModel: ProfileViewModel {
        didSet {
            if let accountViewModel = viewModel.accountViewModel {
                headerImageView.sd_setImage(with: accountViewModel.headerURL) { [weak self] image, _, _, _ in
                    if let image = image, image.size != Self.missingHeaderImageSize {
                        self?.headerButton.isEnabled = true
                    }
                }
                headerImageView.tag = accountViewModel.headerURL.hashValue
                headerButton.accessibilityLabel = String.localizedStringWithFormat(
                    NSLocalizedString("account.header.accessibility-label-%@", comment: ""),
                    accountViewModel.displayName)
                avatarImageView.sd_setImage(with: accountViewModel.avatarURL(profile: true))
                avatarImageView.tag = accountViewModel.avatarURL(profile: true).hashValue
                avatarButton.accessibilityLabel = String.localizedStringWithFormat(
                    NSLocalizedString("account.avatar.accessibility-label-%@", comment: ""),
                    accountViewModel.displayName)

                if !accountViewModel.isSelf, let relationship = accountViewModel.relationship {
                    followsYouLabel.isHidden = !relationship.followedBy
                    mutedLabel.isHidden = !relationship.muting
                    blockedLabel.isHidden = !relationship.blocking
                    followButton.setTitle(
                        NSLocalizedString(
                            accountViewModel.isLocked ? "account.request" : "account.follow",
                            comment: ""),
                        for: .normal)
                    followButton.isHidden = relationship.following || relationship.requested
                    unfollowButton.isHidden = !(relationship.following || relationship.requested)
                    unfollowButton.setTitle(
                        NSLocalizedString(
                            relationship.requested ? "account.request.cancel" : "account.following",
                            comment: ""),
                        for: .normal)

                    if relationship.following, let notifying = relationship.notifying {
                        if notifying {
                            notifyButton.isHidden = true
                            unnotifyButton.isHidden = false
                        } else {
                            notifyButton.isHidden = false
                            unnotifyButton.isHidden = true
                        }
                    } else {
                        notifyButton.isHidden = true
                        unnotifyButton.isHidden = true
                    }

                    relationshipButtonsStackView.isHidden = false
                    unavailableLabel.isHidden = !relationship.blockedBy
                } else {
                    relationshipButtonsStackView.isHidden = true
                    unavailableLabel.isHidden = true
                }

                if accountViewModel.displayName.isEmpty {
                    displayNameLabel.isHidden = true
                } else {
                    let mutableDisplayName = NSMutableAttributedString(string: accountViewModel.displayName)

                    mutableDisplayName.insert(emojis: accountViewModel.emojis,
                                              view: displayNameLabel,
                                              identityContext: viewModel.identityContext)
                    mutableDisplayName.resizeAttachments(toLineHeight: displayNameLabel.font.lineHeight)
                    displayNameLabel.attributedText = mutableDisplayName
                }

                accountLabel.text = accountViewModel.accountName
                lockedImageView.isHidden = !accountViewModel.isLocked

                var accountStackViewAccessibilityLabel = accountViewModel.accountName

                if !lockedImageView.isHidden {
                    accountStackViewAccessibilityLabel
                        .appendWithSeparator(NSLocalizedString("account.locked.accessibility-label", comment: ""))
                }

                if !followsYouLabel.isHidden, let followsYouText = followsYouLabel.text {
                    accountStackViewAccessibilityLabel.appendWithSeparator(followsYouText)
                }

                accountStackView.accessibilityLabel = accountStackViewAccessibilityLabel

                let statusCountFormat: String

                switch viewModel.identityContext.appPreferences.statusWord {
                case .toot:
                    statusCountFormat = NSLocalizedString("statuses.count.toot-%ld", comment: "")
                case .post:
                    statusCountFormat = NSLocalizedString("statuses.count.post-%ld", comment: "")
                }

                statusCountLabel.text = String.localizedStringWithFormat(
                    statusCountFormat,
                    accountViewModel.statusesCount)
                joinedLabel.text = String.localizedStringWithFormat(
                    NSLocalizedString("account.joined-%@", comment: ""),
                    Self.joinedDateFormatter.string(from: accountViewModel.joined))

                for view in fieldsStackView.arrangedSubviews {
                    fieldsStackView.removeArrangedSubview(view)
                    view.removeFromSuperview()
                }

                for identityProof in accountViewModel.identityProofs {
                    let fieldView = AccountFieldView(
                        name: identityProof.provider,
                        value: NSAttributedString(
                            string: identityProof.providerUsername,
                            attributes: [.link: identityProof.profileUrl]),
                        verifiedAt: identityProof.updatedAt,
                        emojis: [],
                        identityContext: viewModel.identityContext)

                    fieldView.valueTextView.delegate = self

                    fieldsStackView.addArrangedSubview(fieldView)
                }

                for field in accountViewModel.fields {
                    let fieldView = AccountFieldView(
                        name: field.name,
                        value: field.value.attributed,
                        verifiedAt: field.verifiedAt,
                        emojis: accountViewModel.emojis,
                        identityContext: viewModel.identityContext)

                    fieldView.valueTextView.delegate = self

                    fieldsStackView.addArrangedSubview(fieldView)
                }

                fieldsStackView.isHidden = accountViewModel.fields.isEmpty && accountViewModel.identityProofs.isEmpty

                let noteFont = UIFont.preferredFont(forTextStyle: .callout)
                let mutableNote = NSMutableAttributedString(attributedString: accountViewModel.note)
                let noteRange = NSRange(location: 0, length: mutableNote.length)
                mutableNote.removeAttribute(.font, range: noteRange)
                mutableNote.addAttributes(
                    [.font: noteFont as Any,
                     .foregroundColor: UIColor.label],
                    range: noteRange)
                mutableNote.insert(emojis: accountViewModel.emojis,
                                   view: noteTextView,
                                   identityContext: viewModel.identityContext)
                mutableNote.resizeAttachments(toLineHeight: noteFont.lineHeight)
                noteTextView.attributedText = mutableNote
                noteTextView.isHidden = false

                followingButton.setAttributedLocalizedTitle(
                    localizationKey: "account.following-count-%ld",
                    count: accountViewModel.followingCount)
                followersButton.setAttributedLocalizedTitle(
                    localizationKey: "account.followers-count-%ld",
                    count: accountViewModel.followersCount)
                followStackView.isHidden = false
            } else {
                noteTextView.isHidden = true
                followStackView.isHidden = true
            }
        }
    }

    init(viewModel: ProfileViewModel) {
        self.viewModel = viewModel

        // Initial size is to avoid unsatisfiable constraint warning
        super.init(frame: .init(origin: .zero, size: .init(width: 300, height: 300)))

        initialSetup()
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        if let pointSize = followingButton.titleLabel?.font.pointSize {
            relationshipButtonsStackView.heightAnchor
                .constraint(equalToConstant: pointSize + .defaultSpacing * 2).isActive = true
        }

        for button in [followButton, unfollowButton] {
            let inset = (button.bounds.height - (button.titleLabel?.bounds.height ?? 0))

            button.contentEdgeInsets = .init(top: 0, left: inset, bottom: 0, right: inset)
        }

        for button in [directMessageButton, followButton, unfollowButton, notifyButton, unnotifyButton] {
            button.layer.cornerRadius = button.bounds.height / 2
        }
    }
}

extension AccountHeaderView: UITextViewDelegate {
    func textView(
        _ textView: UITextView,
        shouldInteractWith URL: URL,
        in characterRange: NSRange,
        interaction: UITextItemInteraction) -> Bool {
        switch interaction {
        case .invokeDefaultAction:
            viewModel.accountViewModel?.urlSelected(URL)
            return false
        case .preview: return false
        case .presentActions: return false
        @unknown default: return false
        }
    }
}

private extension AccountHeaderView {
    static let avatarDimension = CGFloat.avatarDimension * 2
    static let missingHeaderImageSize = CGSize(width: 1, height: 1)
    static let joinedDateFormatter: DateFormatter = {
        let formatter = DateFormatter()

        formatter.dateStyle = .short

        return formatter
    }()

    // swiftlint:disable:next function_body_length
    func initialSetup() {
        let baseStackView = UIStackView()

        addSubview(headerImageBackgroundView)
        headerImageBackgroundView.translatesAutoresizingMaskIntoConstraints = false
        headerImageBackgroundView.backgroundColor = .secondarySystemBackground

        addSubview(headerImageView)
        headerImageView.translatesAutoresizingMaskIntoConstraints = false
        headerImageView.contentMode = .scaleAspectFill
        headerImageView.clipsToBounds = true
        headerImageView.isUserInteractionEnabled = true

        headerImageView.addSubview(headerButton)
        headerButton.translatesAutoresizingMaskIntoConstraints = false
        headerButton.setBackgroundImage(.highlightedButtonBackground, for: .highlighted)

        headerButton.addAction(UIAction { [weak self] _ in self?.viewModel.presentHeader() }, for: .touchUpInside)
        headerButton.isEnabled = false

        let avatarBackgroundViewDimension = Self.avatarDimension + .compactSpacing * 2

        addSubview(avatarBackgroundView)
        avatarBackgroundView.translatesAutoresizingMaskIntoConstraints = false
        avatarBackgroundView.backgroundColor = .systemBackground
        avatarBackgroundView.layer.cornerRadius = avatarBackgroundViewDimension / 2

        avatarBackgroundView.addSubview(avatarImageView)
        avatarImageView.translatesAutoresizingMaskIntoConstraints = false
        avatarImageView.contentMode = .scaleAspectFill
        avatarImageView.clipsToBounds = true
        avatarImageView.isUserInteractionEnabled = true
        avatarImageView.layer.cornerRadius = Self.avatarDimension / 2

        avatarImageView.addSubview(avatarButton)
        avatarButton.translatesAutoresizingMaskIntoConstraints = false
        avatarButton.setBackgroundImage(.highlightedButtonBackground, for: .highlighted)

        avatarButton.addAction(UIAction { [weak self] _ in self?.viewModel.presentAvatar() }, for: .touchUpInside)

        addSubview(relationshipButtonsStackView)
        relationshipButtonsStackView.translatesAutoresizingMaskIntoConstraints = false
        relationshipButtonsStackView.spacing = .defaultSpacing
        relationshipButtonsStackView.addArrangedSubview(UIView())

        for button in [directMessageButton, notifyButton, unnotifyButton, followButton, unfollowButton] {
            relationshipButtonsStackView.addArrangedSubview(button)
            button.titleLabel?.font = .preferredFont(forTextStyle: .headline)
            button.titleLabel?.adjustsFontForContentSizeCategory = true
            button.backgroundColor = .secondarySystemBackground
        }

        directMessageButton.setImage(
            UIImage(
                systemName: "envelope",
                withConfiguration: UIImage.SymbolConfiguration(scale: .small)),
            for: .normal)
        directMessageButton.accessibilityLabel = NSLocalizedString("account.direct-message", comment: "")
        directMessageButton.addAction(
            UIAction { [weak self] _ in self?.viewModel.sendDirectMessage() },
            for: .touchUpInside)

        followButton.setImage(
            UIImage(
                systemName: "person.badge.plus",
                withConfiguration: UIImage.SymbolConfiguration(scale: .small)),
            for: .normal)
        followButton.isHidden = true
        followButton.titleLabel?.adjustsFontSizeToFitWidth = true
        followButton.addAction(
            UIAction { [weak self] _ in self?.viewModel.accountViewModel?.follow() },
            for: .touchUpInside)

        unfollowButton.setImage(
            UIImage(
                systemName: "checkmark",
                withConfiguration: UIImage.SymbolConfiguration(scale: .small)),
            for: .normal)
        unfollowButton.setTitle(NSLocalizedString("account.following", comment: ""), for: .normal)
        unfollowButton.isHidden = true
        unfollowButton.titleLabel?.adjustsFontSizeToFitWidth = true
        unfollowButton.addAction(
            UIAction { [weak self] _ in self?.viewModel.accountViewModel?.confirmUnfollow() },
            for: .touchUpInside)

        notifyButton.setImage(
            UIImage(systemName: "bell",
                    withConfiguration: UIImage.SymbolConfiguration(scale: .medium)),
            for: .normal)
        notifyButton.imageView?.contentMode = .scaleAspectFit
        notifyButton.accessibilityLabel = NSLocalizedString("account.notify", comment: "")
        notifyButton.tintColor = .secondaryLabel
        notifyButton.isHidden = true
        notifyButton.addAction(
            UIAction { [weak self] _ in self?.viewModel.accountViewModel?.notify() },
            for: .touchUpInside)

        unnotifyButton.setImage(
            UIImage(systemName: "bell.fill",
                    withConfiguration: UIImage.SymbolConfiguration(scale: .medium)),
            for: .normal)
        unnotifyButton.accessibilityLabel = NSLocalizedString("account.unnotify", comment: "")
        unnotifyButton.isHidden = true
        unnotifyButton.addAction(
            UIAction { [weak self] _ in self?.viewModel.accountViewModel?.unnotify() },
            for: .touchUpInside)

        addSubview(baseStackView)
        baseStackView.translatesAutoresizingMaskIntoConstraints = false
        baseStackView.axis = .vertical
        baseStackView.spacing = .defaultSpacing

        baseStackView.addArrangedSubview(displayNameLabel)
        displayNameLabel.numberOfLines = 0
        displayNameLabel.font = .preferredFont(forTextStyle: .headline)
        displayNameLabel.adjustsFontForContentSizeCategory = true

        baseStackView.addArrangedSubview(accountStackView)
        accountStackView.spacing = .compactSpacing
        accountStackView.isAccessibilityElement = true

        accountStackView.addArrangedSubview(accountLabel)
        accountLabel.numberOfLines = 0
        accountLabel.font = .preferredFont(forTextStyle: .subheadline)
        accountLabel.adjustsFontForContentSizeCategory = true
        accountLabel.textColor = .secondaryLabel
        accountLabel.setContentHuggingPriority(.required, for: .horizontal)
        accountLabel.setContentCompressionResistancePriority(.required, for: .horizontal)

        accountStackView.addArrangedSubview(lockedImageView)
        lockedImageView.image = UIImage(
            systemName: "lock.fill",
            withConfiguration: UIImage.SymbolConfiguration(scale: .small))
        lockedImageView.tintColor = .secondaryLabel
        lockedImageView.contentMode = .scaleAspectFit

        accountStackView.addArrangedSubview(followsYouLabel)
        followsYouLabel.text = NSLocalizedString("account.follows-you", comment: "")
        followsYouLabel.isHidden = true

        accountStackView.addArrangedSubview(mutedLabel)
        mutedLabel.text = NSLocalizedString("account.muted", comment: "")
        mutedLabel.isHidden = true

        accountStackView.addArrangedSubview(blockedLabel)
        blockedLabel.text = NSLocalizedString("account.blocked", comment: "")
        blockedLabel.isHidden = true

        accountStackView.addArrangedSubview(UIView())

        baseStackView.addArrangedSubview(statusCountJoinedStackView)
        statusCountJoinedStackView.spacing = .compactSpacing

        statusCountJoinedStackView.addArrangedSubview(statusCountLabel)
        statusCountLabel.font = .preferredFont(forTextStyle: .footnote)
        statusCountLabel.adjustsFontForContentSizeCategory = true
        statusCountLabel.textColor = .tertiaryLabel
        statusCountLabel.setContentHuggingPriority(.required, for: .horizontal)
        statusCountLabel.setContentCompressionResistancePriority(.required, for: .horizontal)

        statusCountJoinedStackView.addArrangedSubview(statusCountJoinedSeparatorLabel)
        statusCountJoinedSeparatorLabel.font = .preferredFont(forTextStyle: .footnote)
        statusCountJoinedSeparatorLabel.adjustsFontForContentSizeCategory = true
        statusCountJoinedSeparatorLabel.textColor = .tertiaryLabel
        statusCountJoinedSeparatorLabel.setContentHuggingPriority(.required, for: .horizontal)
        statusCountJoinedSeparatorLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
        statusCountJoinedSeparatorLabel.text = "•"
        statusCountJoinedSeparatorLabel.isAccessibilityElement = false

        statusCountJoinedStackView.addArrangedSubview(joinedLabel)
        joinedLabel.font = .preferredFont(forTextStyle: .footnote)
        joinedLabel.adjustsFontForContentSizeCategory = true
        joinedLabel.textColor = .tertiaryLabel
        joinedLabel.setContentHuggingPriority(.required, for: .horizontal)
        joinedLabel.setContentCompressionResistancePriority(.required, for: .horizontal)

        statusCountJoinedStackView.addArrangedSubview(UIView())

        baseStackView.addArrangedSubview(fieldsStackView)
        fieldsStackView.axis = .vertical
        fieldsStackView.spacing = .hairline
        fieldsStackView.backgroundColor = .separator
        fieldsStackView.clipsToBounds = true
        fieldsStackView.layer.borderColor = UIColor.separator.cgColor
        fieldsStackView.layer.borderWidth = .hairline
        fieldsStackView.layer.cornerRadius = .defaultCornerRadius
        fieldsStackView.isHidden = true

        baseStackView.addArrangedSubview(noteTextView)
        noteTextView.delegate = self

        baseStackView.addArrangedSubview(followStackView)
        followStackView.distribution = .fillEqually

        followingButton.addAction(
            UIAction { [weak self] _ in self?.viewModel.accountViewModel?.followingSelected() },
            for: .touchUpInside)
        followStackView.addArrangedSubview(followingButton)

        followersButton.addAction(
            UIAction { [weak self] _ in self?.viewModel.accountViewModel?.followersSelected() },
            for: .touchUpInside)
        followStackView.addArrangedSubview(followersButton)

        let statusWord = viewModel.identityContext.appPreferences.statusWord

        for (index, collection) in ProfileCollection.allCases.enumerated() {
            segmentedControl.insertSegment(
                action: UIAction(title: collection.title(statusWord: statusWord)) { [weak self] _ in
                    self?.viewModel.collection = collection
                    self?.viewModel.request(maxId: nil, minId: nil, search: nil)
                },
                at: index,
                animated: false)
        }

        segmentedControl.selectedSegmentIndex = 0

        baseStackView.addArrangedSubview(segmentedControl)

        baseStackView.addArrangedSubview(unavailableLabel)
        unavailableLabel.adjustsFontForContentSizeCategory = true
        unavailableLabel.font = .preferredFont(forTextStyle: .title3)
        unavailableLabel.textAlignment = .center
        unavailableLabel.numberOfLines = 0
        unavailableLabel.text = NSLocalizedString("account.unavailable", comment: "")
        unavailableLabel.isHidden = true

        let headerImageAspectRatioConstraint = headerImageView.heightAnchor.constraint(
            equalTo: headerImageView.widthAnchor,
            multiplier: 1 / 3)

        headerImageAspectRatioConstraint.priority = .justBelowMax

        NSLayoutConstraint.activate([
            headerImageAspectRatioConstraint,
            headerImageView.topAnchor.constraint(equalTo: topAnchor),
            headerImageView.leadingAnchor.constraint(equalTo: leadingAnchor),
            headerImageView.trailingAnchor.constraint(equalTo: trailingAnchor),
            headerImageBackgroundView.leadingAnchor.constraint(equalTo: headerImageView.leadingAnchor),
            headerImageBackgroundView.topAnchor.constraint(equalTo: headerImageView.topAnchor),
            headerImageBackgroundView.trailingAnchor.constraint(equalTo: headerImageView.trailingAnchor),
            headerImageBackgroundView.bottomAnchor.constraint(equalTo: headerImageView.bottomAnchor),
            headerButton.leadingAnchor.constraint(equalTo: headerImageView.leadingAnchor),
            headerButton.topAnchor.constraint(equalTo: headerImageView.topAnchor),
            headerButton.bottomAnchor.constraint(equalTo: headerImageView.bottomAnchor),
            headerButton.trailingAnchor.constraint(equalTo: headerImageView.trailingAnchor),
            avatarBackgroundView.heightAnchor.constraint(equalToConstant: avatarBackgroundViewDimension),
            avatarBackgroundView.widthAnchor.constraint(equalToConstant: avatarBackgroundViewDimension),
            avatarBackgroundView.leadingAnchor.constraint(equalTo: readableContentGuide.leadingAnchor),
            avatarBackgroundView.centerYAnchor.constraint(equalTo: headerImageView.bottomAnchor),
            avatarImageView.heightAnchor.constraint(equalToConstant: Self.avatarDimension),
            avatarImageView.widthAnchor.constraint(equalToConstant: Self.avatarDimension),
            avatarImageView.centerXAnchor.constraint(equalTo: avatarBackgroundView.centerXAnchor),
            avatarImageView.centerYAnchor.constraint(equalTo: avatarBackgroundView.centerYAnchor),
            avatarButton.leadingAnchor.constraint(equalTo: avatarImageView.leadingAnchor),
            avatarButton.topAnchor.constraint(equalTo: avatarImageView.topAnchor),
            avatarButton.bottomAnchor.constraint(equalTo: avatarImageView.bottomAnchor),
            avatarButton.trailingAnchor.constraint(equalTo: avatarImageView.trailingAnchor),
            relationshipButtonsStackView.leadingAnchor.constraint(equalTo: avatarBackgroundView.trailingAnchor),
            relationshipButtonsStackView.topAnchor.constraint(
                equalTo: headerImageView.bottomAnchor,
                constant: .defaultSpacing),
            relationshipButtonsStackView.trailingAnchor.constraint(equalTo: readableContentGuide.trailingAnchor),
            directMessageButton.widthAnchor.constraint(equalTo: directMessageButton.heightAnchor),
            notifyButton.widthAnchor.constraint(equalTo: notifyButton.heightAnchor),
            unnotifyButton.widthAnchor.constraint(equalTo: unnotifyButton.heightAnchor),
            baseStackView.topAnchor.constraint(equalTo: avatarBackgroundView.bottomAnchor, constant: .defaultSpacing),
            baseStackView.leadingAnchor.constraint(equalTo: readableContentGuide.leadingAnchor),
            baseStackView.trailingAnchor.constraint(equalTo: readableContentGuide.trailingAnchor),
            baseStackView.bottomAnchor.constraint(equalTo: readableContentGuide.bottomAnchor)
        ])
    }
}
