// Copyright © 2020 Metabolist. All rights reserved.

import Mastodon
import UIKit
import ViewModels

final class ConversationView: UIView {
    let avatarsView = ConversationAvatarsView()
    let displayNamesLabel = AnimatedAttachmentLabel()
    let unreadIndicator = UIImageView(image: UIImage(
                                        systemName: "circlebadge.fill",
                                        withConfiguration: UIImage.SymbolConfiguration(scale: .small)))
    let timeLabel = UILabel()
    let statusBodyView = StatusBodyView()

    private var conversationConfiguration: ConversationContentConfiguration

    init(configuration: ConversationContentConfiguration) {
        conversationConfiguration = configuration

        super.init(frame: .zero)

        initialSetup()
        applyConversationConfiguration()
    }

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

extension ConversationView {
    static func estimatedHeight(width: CGFloat,
                                identityContext: IdentityContext,
                                conversation: Conversation) -> CGFloat {
        guard let status = conversation.lastStatus else { return UITableView.automaticDimension }

        let bodyWidth = width - .defaultSpacing - .avatarDimension

        return .defaultSpacing * 2
            + UIFont.preferredFont(forTextStyle: .headline).lineHeight
            + StatusBodyView.estimatedHeight(
                width: bodyWidth,
                identityContext: identityContext,
                status: status,
                configuration: .default)
    }
}

extension ConversationView: UIContentView {
    var configuration: UIContentConfiguration {
        get { conversationConfiguration }
        set {
            guard let conversationConfiguration = newValue as? ConversationContentConfiguration else { return }

            self.conversationConfiguration = conversationConfiguration

            applyConversationConfiguration()
        }
    }
}

private extension ConversationView {
    // swiftlint:disable:next function_body_length
    func initialSetup() {
        let containerStackView = UIStackView()
        let sideStackView = UIStackView()
        let mainStackView = UIStackView()
        let namesTimeStackView = UIStackView()

        addSubview(containerStackView)
        containerStackView.translatesAutoresizingMaskIntoConstraints = false
        containerStackView.spacing = .defaultSpacing

        sideStackView.alignment = .top
        sideStackView.spacing = .compactSpacing
        sideStackView.addArrangedSubview(avatarsView)
        containerStackView.addArrangedSubview(sideStackView)

        namesTimeStackView.spacing = .compactSpacing
        namesTimeStackView.alignment = .top
        namesTimeStackView.addArrangedSubview(displayNamesLabel)
        namesTimeStackView.addArrangedSubview(unreadIndicator)
        namesTimeStackView.addArrangedSubview(timeLabel)

        mainStackView.axis = .vertical
        mainStackView.spacing = .compactSpacing
        mainStackView.addArrangedSubview(namesTimeStackView)
        mainStackView.addArrangedSubview(statusBodyView)
        containerStackView.addArrangedSubview(mainStackView)

        displayNamesLabel.font = .preferredFont(forTextStyle: .headline)
        displayNamesLabel.adjustsFontForContentSizeCategory = true
        displayNamesLabel.numberOfLines = 0

        unreadIndicator.contentMode = .scaleAspectFit
        unreadIndicator.setContentHuggingPriority(.required, for: .horizontal)

        timeLabel.font = .preferredFont(forTextStyle: .subheadline)
        timeLabel.adjustsFontForContentSizeCategory = true
        timeLabel.textColor = .secondaryLabel
        timeLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
        timeLabel.setContentHuggingPriority(.required, for: .horizontal)

        statusBodyView.alpha = 0.5
        statusBodyView.isUserInteractionEnabled = false

        let avatarsHeightConstraint = avatarsView.heightAnchor.constraint(equalToConstant: .avatarDimension)

        avatarsHeightConstraint.priority = .justBelowMax

        NSLayoutConstraint.activate([
            containerStackView.leadingAnchor.constraint(equalTo: readableContentGuide.leadingAnchor),
            containerStackView.topAnchor.constraint(equalTo: readableContentGuide.topAnchor),
            containerStackView.trailingAnchor.constraint(equalTo: readableContentGuide.trailingAnchor),
            containerStackView.bottomAnchor.constraint(equalTo: readableContentGuide.bottomAnchor),
            avatarsView.widthAnchor.constraint(equalToConstant: .avatarDimension),
            avatarsHeightConstraint,
            sideStackView.widthAnchor.constraint(equalToConstant: .avatarDimension)
        ])

        isAccessibilityElement = true
    }

    func applyConversationConfiguration() {
        let viewModel = conversationConfiguration.viewModel
        let displayNames = ListFormatter.localizedString(byJoining: viewModel.accountViewModels.map(\.displayName))
        let mutableDisplayNames = NSMutableAttributedString(string: displayNames)

        mutableDisplayNames.insert(
            emojis: viewModel.accountViewModels.map(\.emojis).reduce([], +),
            view: displayNamesLabel,
            identityContext: viewModel.identityContext)
        mutableDisplayNames.resizeAttachments(toLineHeight: displayNamesLabel.font.lineHeight)

        unreadIndicator.isHidden = !viewModel.isUnread
        displayNamesLabel.attributedText = mutableDisplayNames
        timeLabel.text = viewModel.statusViewModel?.time
        timeLabel.accessibilityLabel = viewModel.statusViewModel?.accessibilityTime
        statusBodyView.viewModel = viewModel.statusViewModel
        avatarsView.viewModel = viewModel

        let accessibilityAttributedLabel = NSMutableAttributedString(attributedString: mutableDisplayNames)

        if viewModel.isUnread {
            accessibilityAttributedLabel.appendWithSeparator(NSLocalizedString("conversation.unread", comment: ""))
        }

        if let statusBodyAccessibilityAttributedLabel = statusBodyView.accessibilityAttributedLabel {
            accessibilityAttributedLabel.appendWithSeparator(statusBodyAccessibilityAttributedLabel)
        }

        if let accessibilityTime = viewModel.statusViewModel?.accessibilityTime {
            accessibilityAttributedLabel.appendWithSeparator(accessibilityTime)
        }

        self.accessibilityAttributedLabel = accessibilityAttributedLabel
    }
}