// Copyright © 2020 Metabolist. All rights reserved.

import Mastodon
import SDWebImage
import UIKit
import ViewModels

final class AccountView: UIView {
    let avatarImageView = SDAnimatedImageView()
    let displayNameLabel = AnimatedAttachmentLabel()
    let accountLabel = UILabel()
    let noteTextView = TouchFallthroughTextView()
    let acceptFollowRequestButton = UIButton()
    let rejectFollowRequestButton = UIButton()
    let muteButton = UIButton(type: .system)
    let unmuteButton = UIButton(type: .system)
    let blockButton = UIButton(type: .system)
    let unblockButton = UIButton(type: .system)

    private var accountConfiguration: AccountContentConfiguration

    init(configuration: AccountContentConfiguration) {
        self.accountConfiguration = configuration

        super.init(frame: .zero)

        initialSetup()
        applyAccountConfiguration()
    }

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

extension AccountView {
    static func estimatedHeight(width: CGFloat,
                                account: Account,
                                configuration: CollectionItem.AccountConfiguration) -> CGFloat {
        var height = CGFloat.defaultSpacing * 2
            + .compactSpacing
            + account.displayName.height(width: width, font: .preferredFont(forTextStyle: .headline))
            + account.acct.height(width: width, font: .preferredFont(forTextStyle: .subheadline))

        if configuration == .withNote {
            height += .compactSpacing + account.note.attributed.string.height(
                width: width,
                font: .preferredFont(forTextStyle: .callout))
        }

        return max(height, .avatarDimension + .defaultSpacing * 2)
    }
}

extension AccountView: UIContentView {
    var configuration: UIContentConfiguration {
        get { accountConfiguration }
        set {
            guard let accountConfiguration = newValue as? AccountContentConfiguration else { return }

            self.accountConfiguration = accountConfiguration

            applyAccountConfiguration()
        }
    }
}

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

private extension AccountView {
    // swiftlint:disable:next function_body_length
    func initialSetup() {
        let stackView = UIStackView()

        addSubview(stackView)
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.spacing = .defaultSpacing
        stackView.alignment = .top

        stackView.addArrangedSubview(avatarImageView)
        avatarImageView.layer.cornerRadius = .avatarDimension / 2
        avatarImageView.clipsToBounds = true

        let verticalStackView = UIStackView()

        stackView.addArrangedSubview(verticalStackView)
        verticalStackView.translatesAutoresizingMaskIntoConstraints = false
        verticalStackView.axis = .vertical
        verticalStackView.spacing = .compactSpacing
        verticalStackView.addArrangedSubview(displayNameLabel)
        verticalStackView.addArrangedSubview(accountLabel)
        verticalStackView.addArrangedSubview(noteTextView)
        displayNameLabel.numberOfLines = 0
        displayNameLabel.font = .preferredFont(forTextStyle: .headline)
        displayNameLabel.adjustsFontForContentSizeCategory = true
        accountLabel.numberOfLines = 0
        accountLabel.font = .preferredFont(forTextStyle: .subheadline)
        accountLabel.adjustsFontForContentSizeCategory = true
        accountLabel.textColor = .secondaryLabel
        noteTextView.backgroundColor = .clear
        noteTextView.delegate = self

        let largeTitlePointSize = UIFont.preferredFont(forTextStyle: .largeTitle).pointSize

        stackView.addArrangedSubview(acceptFollowRequestButton)
        acceptFollowRequestButton.setImage(
            UIImage(systemName: "checkmark.circle",
                    withConfiguration: UIImage.SymbolConfiguration(pointSize: largeTitlePointSize)),
            for: .normal)
        acceptFollowRequestButton.setContentHuggingPriority(.required, for: .horizontal)
        acceptFollowRequestButton.addAction(
            UIAction { [weak self] _ in self?.accountConfiguration.viewModel.acceptFollowRequest() },
            for: .touchUpInside)

        stackView.addArrangedSubview(rejectFollowRequestButton)
        rejectFollowRequestButton.setImage(
            UIImage(systemName: "xmark.circle",
                    withConfiguration: UIImage.SymbolConfiguration(pointSize: largeTitlePointSize)),
            for: .normal)
        rejectFollowRequestButton.tintColor = .systemRed
        rejectFollowRequestButton.setContentHuggingPriority(.required, for: .horizontal)
        rejectFollowRequestButton.addAction(
            UIAction { [weak self] _ in self?.accountConfiguration.viewModel.rejectFollowRequest() },
            for: .touchUpInside)

        stackView.addArrangedSubview(muteButton)
        muteButton.setTitle(NSLocalizedString("account.mute", comment: ""), for: .normal)
        muteButton.titleLabel?.adjustsFontForContentSizeCategory = true
        muteButton.titleLabel?.font = .preferredFont(forTextStyle: .callout)
        muteButton.addAction(
            UIAction { [weak self] _ in self?.accountConfiguration.viewModel.confirmMute() },
            for: .touchUpInside)
        muteButton.isHidden = true

        stackView.addArrangedSubview(unmuteButton)
        unmuteButton.setTitle(NSLocalizedString("account.unmute", comment: ""), for: .normal)
        unmuteButton.titleLabel?.adjustsFontForContentSizeCategory = true
        unmuteButton.titleLabel?.font = .preferredFont(forTextStyle: .callout)
        unmuteButton.addAction(
            UIAction { [weak self] _ in self?.accountConfiguration.viewModel.confirmUnmute() },
            for: .touchUpInside)
        unmuteButton.isHidden = true

        stackView.addArrangedSubview(blockButton)
        blockButton.setTitle(NSLocalizedString("account.block", comment: ""), for: .normal)
        blockButton.titleLabel?.adjustsFontForContentSizeCategory = true
        blockButton.titleLabel?.font = .preferredFont(forTextStyle: .callout)
        blockButton.addAction(
            UIAction { [weak self] _ in self?.accountConfiguration.viewModel.confirmBlock() },
            for: .touchUpInside)
        blockButton.isHidden = true

        stackView.addArrangedSubview(unblockButton)
        unblockButton.setTitle(NSLocalizedString("account.unblock", comment: ""), for: .normal)
        unblockButton.titleLabel?.adjustsFontForContentSizeCategory = true
        unblockButton.titleLabel?.font = .preferredFont(forTextStyle: .callout)
        unblockButton.addAction(
            UIAction { [weak self] _ in self?.accountConfiguration.viewModel.confirmUnblock() },
            for: .touchUpInside)
        unblockButton.isHidden = true

        NSLayoutConstraint.activate([
            avatarImageView.widthAnchor.constraint(equalToConstant: .avatarDimension),
            avatarImageView.heightAnchor.constraint(equalToConstant: .avatarDimension),
            acceptFollowRequestButton.widthAnchor.constraint(greaterThanOrEqualToConstant: .avatarDimension),
            acceptFollowRequestButton.heightAnchor.constraint(greaterThanOrEqualToConstant: .avatarDimension),
            rejectFollowRequestButton.widthAnchor.constraint(greaterThanOrEqualToConstant: .avatarDimension),
            rejectFollowRequestButton.heightAnchor.constraint(greaterThanOrEqualToConstant: .avatarDimension),
            muteButton.heightAnchor.constraint(greaterThanOrEqualToConstant: .avatarDimension),
            unmuteButton.heightAnchor.constraint(greaterThanOrEqualToConstant: .avatarDimension),
            blockButton.heightAnchor.constraint(greaterThanOrEqualToConstant: .avatarDimension),
            unblockButton.heightAnchor.constraint(greaterThanOrEqualToConstant: .avatarDimension),
            stackView.leadingAnchor.constraint(equalTo: readableContentGuide.leadingAnchor),
            stackView.topAnchor.constraint(equalTo: readableContentGuide.topAnchor),
            stackView.bottomAnchor.constraint(equalTo: readableContentGuide.bottomAnchor),
            stackView.trailingAnchor.constraint(equalTo: readableContentGuide.trailingAnchor)
        ])

        isAccessibilityElement = true
    }

    // swiftlint:disable:next function_body_length cyclomatic_complexity
    func applyAccountConfiguration() {
        let viewModel = accountConfiguration.viewModel

        avatarImageView.sd_setImage(with: viewModel.avatarURL(profile: false))

        let mutableDisplayName = NSMutableAttributedString(string: viewModel.displayName)

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

        displayNameLabel.isHidden = viewModel.displayName.isEmpty

        accountLabel.text = viewModel.accountName

        if viewModel.configuration == .withNote {
            let noteFont = UIFont.preferredFont(forTextStyle: .callout)
            let mutableNote = NSMutableAttributedString(attributedString: viewModel.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: viewModel.emojis, view: noteTextView, identityContext: viewModel.identityContext)
            mutableNote.resizeAttachments(toLineHeight: noteFont.lineHeight)

            noteTextView.attributedText = mutableNote
            noteTextView.isHidden = false
        } else {
            noteTextView.isHidden = true
        }

        let isFollowRequest = viewModel.configuration == .followRequest

        acceptFollowRequestButton.isHidden = !isFollowRequest
        rejectFollowRequestButton.isHidden = !isFollowRequest

        if let relationship = viewModel.relationship {
            if viewModel.configuration == .mute {
                muteButton.isHidden = relationship.muting
                unmuteButton.isHidden = !relationship.muting
                blockButton.isHidden = true
                unblockButton.isHidden = true
            } else if viewModel.configuration == .block {
                muteButton.isHidden = true
                unmuteButton.isHidden = true
                blockButton.isHidden = relationship.blocking
                unblockButton.isHidden = !relationship.blocking
            }
        } else {
            muteButton.isHidden = true
            unmuteButton.isHidden = true
            blockButton.isHidden = true
            unblockButton.isHidden = true
        }

        let accessibilityAttributedLabel = NSMutableAttributedString(string: "")

        if !displayNameLabel.isHidden, let displayName = displayNameLabel.attributedText {
            accessibilityAttributedLabel.append(displayName)
            accessibilityAttributedLabel.appendWithSeparator(viewModel.accountName)
        } else {
            accessibilityAttributedLabel.appendWithSeparator(viewModel.accountName)
        }

        if !noteTextView.isHidden, let note = noteTextView.attributedText {
            accessibilityAttributedLabel.appendWithSeparator(note)
        }

        self.accessibilityAttributedLabel = accessibilityAttributedLabel

        if isFollowRequest {
            accessibilityCustomActions = [
                UIAccessibilityCustomAction(
                    name: NSLocalizedString(
                        "account.accept-follow-request-button.accessibility-label",
                        comment: "")) { [weak self] _ in
                        self?.accountConfiguration.viewModel.acceptFollowRequest()

                        return true
                    },
                UIAccessibilityCustomAction(
                    name: NSLocalizedString(
                        "account.reject-follow-request-button.accessibility-label",
                        comment: "")) { [weak self] _ in
                        self?.accountConfiguration.viewModel.rejectFollowRequest()

                        return true
                    }]
        } else if viewModel.configuration == .mute, let relationship = viewModel.relationship {
            if relationship.muting {
                accessibilityCustomActions = [
                    UIAccessibilityCustomAction(
                        name: NSLocalizedString(
                            "account.unmute",
                            comment: "")) { [weak self] _ in
                            self?.accountConfiguration.viewModel.confirmUnmute()

                            return true
                        }]
            } else {
                accessibilityCustomActions = [
                    UIAccessibilityCustomAction(
                        name: NSLocalizedString(
                            "account.mute",
                            comment: "")) { [weak self] _ in
                            self?.accountConfiguration.viewModel.confirmMute()

                            return true
                        }]
            }
        } else if viewModel.configuration == .block, let relationship = viewModel.relationship {
            if relationship.blocking {
                accessibilityCustomActions = [
                    UIAccessibilityCustomAction(
                        name: NSLocalizedString(
                            "account.unblock",
                            comment: "")) { [weak self] _ in
                            self?.accountConfiguration.viewModel.confirmUnblock()

                            return true
                        }]
            } else {
                accessibilityCustomActions = [
                    UIAccessibilityCustomAction(
                        name: NSLocalizedString(
                            "account.block",
                            comment: "")) { [weak self] _ in
                            self?.accountConfiguration.viewModel.confirmBlock()

                            return true
                        }]
            }
        } else {
            accessibilityCustomActions = []
        }
    }
}
