import APIClient
import Localization
import StatsigClient
import SwiftUI

public struct ClipDetailsArtistDetailsView: View {
    let avatarImageUrl: String?
    let artistName: String?
    let artistFollowerCount: String?
    let artistSongCount: String?
    let authorTapped: () -> Void
    let followTapped: () -> Void
    let isFollowing: Bool
    let isCurrentUser: Bool
    var topClips: [Clip]?
    let playTopClipTapped: ([Clip], Int) -> Void
    var isPlaying: Bool
    var playingClipId: Clip.ID?

    var formattedStats: String {
        guard let artistFollowerCount = artistFollowerCount,
              let artistSongCount = artistSongCount else { return "" }
        return "\(artistFollowerCount) \(L10n.FeatureOmniPlayer.followers) \u{00B7} \(artistSongCount) \(L10n.FeatureOmniPlayer.songs)"
    }

    @State var isAnimatingWaveform = false
    @State var isLoading = false

    public init(avatarImageUrl: String?, artistName: String?, artistFollowerCount: String?, artistSongCount: String?, authorTapped: @escaping () -> Void, followTapped: @escaping () -> Void, isFollowing: Bool, isCurrentUser: Bool, topClips: [Clip]?, playTopClipTapped: @escaping ([Clip], Int) -> Void, isPlaying: Bool, playingClipId: Clip.ID? = nil) {
        self.avatarImageUrl = avatarImageUrl
        self.artistName = artistName
        self.artistFollowerCount = artistFollowerCount
        self.artistSongCount = artistSongCount
        self.authorTapped = authorTapped
        self.followTapped = followTapped
        self.isFollowing = isFollowing
        self.isCurrentUser = isCurrentUser
        self.topClips = topClips
        self.playTopClipTapped = playTopClipTapped
        self.isPlaying = isPlaying
        self.playingClipId = playingClipId
    }

    public var body: some View {
        VStack(spacing: 12) {
            HStack {
                artistInfoCard
                    .onTapGesture {
                        authorTapped()
                    }

                Spacer()
                if !isCurrentUser {
                    followButton
                }
            }

            if FeatureFlag.clips.omniplayerTopSongs {
                if topClips != nil {
                    topClipsList
                }
            }
        }
        .padding(16)
        .glassBackground(shape: .rect(cornerRadius: 16), fallbackStyle: Color.SemanticV2.backgroundFogThin)
    }

    @ViewBuilder
    var artistInfoCard: some View {
        HStack {
            RemoteImage(url: avatarImageUrl, fallbackId: artistName)
                .frame(width: 38, height: 38)
                .cornerRadius(.infinity)
                .padding(0.5)
                .background(
                    Circle()
                        .fill(Material.ultraThinMaterial)
                        .strokeBorder(Color.SemanticV2.backgroundGlassDense, lineWidth: 0.5)
                )

            VStack(alignment: .leading, spacing: 2) {
                Text(artistName ?? "")
                    .foregroundColor(.SemanticV2.foregroundPrimary)
                    .typographyV1(.caption2)
                    .lineLimit(2)
                    .truncationMode(.tail)
                Text(formattedStats)
                    .foregroundColor(.SemanticV2.foregroundTertiaryGlass)
                    .typographyV1(.caption4.neueMontrealRegular())
                    .lineLimit(1)
            }
        }
    }

    @ViewBuilder
    var followButton: some View {
        Button {
            followTapped()
        } label: {
            Text(isFollowing ? L10n.FeatureOmniPlayer.following : L10n.FeatureOmniPlayer.follow)
                .foregroundColor(isFollowing ? .SemanticV2.foregroundTertiaryGlass : .SemanticV2.foregroundPrimaryOnLight)
                .typographyV1(.caption2)
        }
        .padding(.horizontal, 12)
        .padding(.vertical, 6)
        .background(
            RoundedRectangle(cornerRadius: 16)
                .fill(isFollowing ? Color.clear : Color.white)
                .strokeBorder(Color.SemanticV2.foregroundTertiaryGlass, lineWidth: 1)
        )
    }

    @ViewBuilder
    var topClipsList: some View {
        HStack(spacing: 12) {
            if let topClips = topClips {
                ForEach(0 ..< min(3, topClips.count), id: \.self) { index in
                    let clip = topClips[index]
                    // display cards if finished loading and bave at least 1 clip
                    if !isLoading && !topClips.isEmpty {
                        topSongCard(withClip: clip)
                            .onAppear {
                                isLoading = false
                            }
                    } else if isLoading && !topClips.isEmpty {
                        RoundedRectangle(cornerRadius: 12)
                            .fill(Color.SemanticV2.backgroundTertiaryGlass)
                    }
                }
            }
        }
        .frame(height: 140)
    }

    @ViewBuilder
    func topSongCard(withClip clip: Clip) -> some View {
        Button {
            guard let topClips = topClips, let clipIndex = topClips.firstIndex(of: clip) else { return }
            playTopClipTapped(topClips, clipIndex)
        } label: {
            ZStack {
                clipAsset(assetURL: clip.imageUrl)

                clipDisplayedContent(clip: clip)
            }
            .overlay(alignment: .center) {
                if playingClipId == clip.id {
                    Waveform(isAnimating: isAnimatingWaveform)
                        .frame(width: 16, height: 16)
                        .environment(\.colorScheme, .dark)
                        .transition(.opacity)
                        .onDisappear { isAnimatingWaveform = false }
                        .onAppear { isAnimatingWaveform = isPlaying }
                        .onChange(of: isPlaying) { _, isPlaying in isAnimatingWaveform = isPlaying }
                        .onChange(of: playingClipId) { _, newPlayingClipId in
                            if newPlayingClipId != clip.id {
                                isAnimatingWaveform = false
                            } else {
                                isAnimatingWaveform = isPlaying
                            }
                        }
                }
            }
        }
    }

    @ViewBuilder
    private func clipAsset(assetURL: String?) -> some View {
        ZStack {
            RemoteImage(url: assetURL, fallbackId: artistName)
            LinearGradient(
                stops: [
                    .init(color: .black.opacity(0.5), location: 0.0),
                    .init(color: .clear, location: 0.5),
                    .init(color: .black.opacity(0.5), location: 1.0),
                ],
                startPoint: .top,
                endPoint: .bottom
            )
            .blendMode(.multiply)
        }
        .cornerRadius(12)
        .clipped()
    }

    @ViewBuilder
    private func clipDisplayedContent(clip: Clip) -> some View {
        VStack(alignment: .leading) {
            clipPlayCount(playCount: clip.playCount)
            Spacer()
            clipDetails(clip: clip)
        }
        .padding(.vertical, 8)
        .padding(.horizontal, 7)
        .frame(maxWidth: .infinity, alignment: .leading)
    }

    @ViewBuilder
    private func clipPlayCount(playCount: Int) -> some View {
        HStack(spacing: 4) {
            Image.Omniplayer.play
                .renderingMode(.template)
                .resizable()
                .frame(width: 16, height: 16)
                .foregroundStyle(Color.SemanticV2.foregroundPrimaryGlass)
            Text(formatPlayCount(playCount))
                .typographyV1(.caption3)
                .foregroundStyle(Color.SemanticV2.foregroundPrimaryGlass)
        }
    }

    @ViewBuilder
    private func clipDetails(clip: Clip) -> some View {
        VStack(alignment: .leading, spacing: 4) {
            Text(clip.title)
                .typographyV1(.caption2)
                .lineLimit(2)
                .multilineTextAlignment(.leading)
                .foregroundStyle(Color.SemanticV2.foregroundPrimary)

            // Prioritize display tags, song tags fallback
            if clip.highLevelModel == .v5 || clip.highLevelModel == .v4_5 || clip.highLevelModel == .v4_5Plus {
                let displayText = (clip.displayTags?.isEmpty == false) ?
                    clip.displayTags?.capitalized :
                    (
                        (clip.tags.isEmpty == false) ?
                            clip.tags.capitalized : nil
                    )

                if let text = displayText {
                    TagText(text)
                        .typographyV1(.caption4)
                        .lineLimit(1)
                        .foregroundStyle(Color.SemanticV2.foregroundTertiaryGlass)
                }
            }
        }
    }

    func formatPlayCount(_ count: Int) -> String {
        if count >= 1000 {
            let rounded = Double(count) / 1000.0
            return String(format: "%.1f", rounded).replacingOccurrences(of: ".0", with: "") + "k"
        }
        return String(count)
    }
}

public struct TagText: View {
    let text: String
    @State var tagWidth: CGFloat = 250.0
    var fontSize: CGFloat
    var font: TypographyV1
    var textColor: Color

    public init(
        _ text: String,
        fontSize: CGFloat = 16,
        font: TypographyV1 = .body2,
        textColor: Color = .SemanticV2.foregroundTertiary
    ) {
        self.text = text
        self.fontSize = fontSize
        self.font = font
        self.textColor = textColor
    }

    public var body: some View {
        Text(tokenise(text, maxWidth: tagWidth))
            .typographyV1(font)
            .foregroundColor(textColor)
            .lineLimit(1)
            .frame(maxWidth: .infinity, alignment: .leading)
            .readSize {
                tagWidth = $0.width
            }
    }

    func tokenise(_ ins: String, maxWidth: CGFloat) -> String {
        let newLineRegex = #/[\n]/#
        var tagString = ins.replacing(newLineRegex, with: " ")
        var tagCount = 0
        let tags = ins
            .replacingOccurrences(of: ",", with: ", ")
            .split(separator: " ")

        repeat {
            if tagCount > 0 {
                var temptags = tags
                temptags.replaceSubrange(tags.count - tagCount ..< tags.count, with: ["+\(tagCount)"])
                tagString = temptags.joined(separator: " ")
            }
            tagCount += 1
        } while tagString.boundingBox(with: .systemFont(ofSize: fontSize)).width > maxWidth
            && tagCount < tags.count
        return tagString
    }
}
