import SwiftUI
import Models

/// A utility that creates a suitable combined accessibility label for a `StatusRowView` that is not focused.
@MainActor
struct StatusRowAccessibilityLabel {
  let viewModel: StatusRowViewModel

  var hasSpoiler: Bool {
    viewModel.displaySpoiler && viewModel.finalStatus.spoilerText.asRawText.isEmpty == false
  }

  var isReply: Bool {
    if let accountId = viewModel.status.inReplyToAccountId, viewModel.status.mentions.contains(where: { $0.id == accountId }) {
      return true
    }
    return false
  }

  var isBoost: Bool {
    viewModel.status.reblog != nil
  }

  var filter: Filter? {
    guard viewModel.isFiltered else {
      return nil
    }
    return viewModel.filter?.filter
  }

  func finalLabel() -> Text {
    if let filter {
      switch filter.filterAction {
      case .warn:
        Text("status.filter.filtered-by-\(filter.title)")
      case .hide:
        Text("")
      }
    } else {
      userNamePreamble() +
        Text(hasSpoiler
          ? viewModel.finalStatus.spoilerText.asRawText
          : viewModel.finalStatus.content.asRawText
        ) +
        Text(hasSpoiler
          ? "status.editor.spoiler"
          : ""
        ) + Text(", ") +
        pollText() +
        imageAltText() +
        Text(viewModel.finalStatus.createdAt.relativeFormatted) + Text(", ") +
        Text("status.summary.n-replies \(viewModel.finalStatus.repliesCount)") + Text(", ") +
        Text("status.summary.n-boosts \(viewModel.finalStatus.reblogsCount)") + Text(", ") +
        Text("status.summary.n-favorites \(viewModel.finalStatus.favouritesCount)")
    }
  }

  func userNamePreamble() -> Text {
    switch (isReply, isBoost) {
    case (true, false):
      Text("accessibility.status.a-replied-to-\(finalUserDisplayName())") + Text(" ")
    case (_, true):
      Text("accessibility.status.a-boosted-b-\(userDisplayName())-\(finalUserDisplayName())") + Text(", ")
    default:
      Text(userDisplayName()) + Text(", ")
    }
  }

  func userDisplayName() -> String {
    viewModel.status.account.displayNameWithoutEmojis.count < 4
      ? viewModel.status.account.safeDisplayName
      : viewModel.status.account.displayNameWithoutEmojis
  }

  func finalUserDisplayName() -> String {
    viewModel.finalStatus.account.displayNameWithoutEmojis.count < 4
      ? viewModel.finalStatus.account.safeDisplayName
      : viewModel.finalStatus.account.displayNameWithoutEmojis
  }

  func imageAltText() -> Text {
    let descriptions = viewModel.finalStatus.mediaAttachments
      .compactMap(\.description)

    if descriptions.count == 1 {
      return Text("accessibility.image.alt-text-\(descriptions[0])") + Text(", ")
    } else if descriptions.count > 1 {
      return Text("accessibility.image.alt-text-\(descriptions[0])") + Text(", ") + Text("accessibility.image.alt-text-more.label") + Text(", ")
    } else if viewModel.finalStatus.mediaAttachments.isEmpty == false {
      let differentTypes = Set(viewModel.finalStatus.mediaAttachments.compactMap(\.localizedTypeDescription)).sorted()
      return Text("accessibility.status.contains-media.label-\(ListFormatter.localizedString(byJoining: differentTypes))") + Text(", ")
    } else {
      return Text("")
    }
  }

  func pollText() -> Text {
    if let poll = viewModel.finalStatus.poll {
      let showPercentage = poll.expired || poll.voted ?? false
      let title: LocalizedStringKey = poll.expired
        ? "accessibility.status.poll.finished.label"
        : "accessibility.status.poll.active.label"

      return poll.options.enumerated().reduce(into: Text(title)) { text, pair in
        let (index, option) = pair
        let selected = poll.ownVotes?.contains(index) ?? false
        let percentage = poll.safeVotersCount > 0 && option.votesCount != nil
          ? Int(round(Double(option.votesCount!) / Double(poll.safeVotersCount) * 100))
          : 0

        text = text +
          Text(selected ? "accessibility.status.poll.selected.label" : "") +
          Text(", ") +
          Text("accessibility.status.poll.option-prefix-\(index + 1)-of-\(poll.options.count)") +
          Text(", ") +
          Text(option.title) +
          Text(showPercentage ? ", \(percentage)%. " : ". ")
      }
    }
    return Text("")
  }
}
