import APIClient
import StatsigClient
import SwiftUI

// TODO: At some point look into using a list of views to format text instead of AttributedString. A list of views allow to add custom animations!
public enum InteractiveTextFormatter {
    public struct Config {
        public let baseFont: TypographyV1
        public let baseColor: Color
        public let mentionFont: TypographyV1
        public let mentionColor: Color
        public let trackTimestampLinkFont: TypographyV1
        public let trackTimestampLinkColor: Color
        public let mentionsEnabled: Bool
        public let trackTimestampLinkEnabled: Bool

        init(
            baseFont: TypographyV1,
            baseColor: Color,
            mentionFont: TypographyV1,
            mentionColor: Color,
            timestampFont: TypographyV1,
            timestampColor: Color,
            mentionsEnabled: Bool = true,
            trackTimestampLinkEnabled: Bool = false
        ) {
            self.baseFont = baseFont
            self.baseColor = baseColor
            self.mentionFont = mentionFont
            self.mentionColor = mentionColor
            self.trackTimestampLinkFont = timestampFont
            self.trackTimestampLinkColor = timestampColor
            self.mentionsEnabled = mentionsEnabled
            self.trackTimestampLinkEnabled = trackTimestampLinkEnabled
        }
    }

    public static func formatCaptionText(
        _ content: String,
        mentions: [CaptionUserMention],
        config: Config
    ) -> AttributedString {
        formatText(content, mentions: mentions, config: config)
    }

    public static func formatCommentText(
        _ content: String,
        mentions: [CommentUserMention],
        config: Config = .comment
    ) -> AttributedString {
        formatText(content, mentions: mentions, config: config)
    }
}

public extension InteractiveTextFormatter.Config {
    static let playerCaption = InteractiveTextFormatter.Config(
        baseFont: .playerCaption,
        baseColor: .SemanticV1.textPrimary,
        mentionFont: .playerCaption.ppNeueMontrealBold(),
        mentionColor: .SemanticV1.textPrimary,
        timestampFont: .playerCaption.ppNeueMontrealBold(),
        timestampColor: .SemanticV1.textPrimary,
        mentionsEnabled: true,
        trackTimestampLinkEnabled: false
    )

    static let pinnedCaption = InteractiveTextFormatter.Config(
        baseFont: .pinnedCaption,
        baseColor: .SemanticV1.textPrimary,
        mentionFont: .pinnedCaption.ppNeueMontrealBold(),
        mentionColor: .SemanticV1.textPrimary,
        timestampFont: .pinnedCaption.ppNeueMontrealBold(),
        timestampColor: .SemanticV1.textPrimary,
        mentionsEnabled: true,
        trackTimestampLinkEnabled: false
    )

    static let comment = InteractiveTextFormatter.Config(
        baseFont: .comment,
        baseColor: .SemanticV1.textPrimary,
        mentionFont: .caption2.ppNeueMontrealBold(),
        mentionColor: .SemanticV1.textPrimary,
        timestampFont: .caption2.ppNeueMontrealBold(),
        timestampColor: .SemanticV1.textPrimary,
        mentionsEnabled: true,
        trackTimestampLinkEnabled: true
    )

    static let hookComment = InteractiveTextFormatter.Config(
        baseFont: .comment,
        baseColor: .SemanticV1.textPrimary,
        mentionFont: .caption2.ppNeueMontrealBold(),
        mentionColor: .SemanticV1.textPrimary,
        timestampFont: .caption2.ppNeueMontrealBold(),
        timestampColor: .SemanticV1.textPrimary,
        mentionsEnabled: true,
        trackTimestampLinkEnabled: false
    )
}

private extension TypographyV1 {
    static let pinnedCaption: TypographyV1 = .init(
        name: "Pinned Caption",
        size: 13,
        style: .body,
        weight: .neueMontrealMedium,
        kerning: 0.4,
        lineHeight: 16
    )

    static let comment: TypographyV1 = .caption2
}

extension InteractiveTextFormatter {
    enum Constants {
        static let baseUserTappedURLString: String = "suno://user-tapped/"
        static let baseCommentTrackTimestampTappedURLString: String = "suno://track-timestamp-tapped/"
    }

    /// Transforms the plaintext according to the config settings into a formatted AttributedString with tappable links.
    /// Formats comment/caption user mentions and comment track timestamps.
    ///
    /// ```
    /// let comment = "Thanks to John Doe and @alice for the inspiration at 1:20!"
    /// let mentions = [
    ///     CommentUserMention(start: 10, end: 18, handle: "johndoe"),
    ///     CommentUserMention(start: 23, end: 29, handle: "alice"),
    /// ]
    /// let result = formatText(comment, mentions: mentions, config: .comment)
    /// // Result: "John Doe", "@alice", and "1:20" become formatted and clickable links.
    /// ```
    static func formatText<T: UserMention>(
        _ content: String,
        mentions: [T],
        config: Config
    ) -> AttributedString {
        var attributedString = AttributedString(content)
        attributedString.font = config.baseFont.uiFont
        attributedString.foregroundColor = config.baseColor

        // Format user mentions
        if config.mentionsEnabled {
            for mention in mentions {
                guard mention.start >= 0 && mention.end <= content.count && mention.start < mention.end else { continue }

                let startIndex = attributedString.index(attributedString.startIndex, offsetByCharacters: mention.start)
                let endIndex = attributedString.index(attributedString.startIndex, offsetByCharacters: min(mention.end, attributedString.characters.count))
                let mentionRange = startIndex ..< endIndex

                // Format the mention
                attributedString[mentionRange].font = config.mentionFont.uiFont
                attributedString[mentionRange].foregroundColor = config.mentionColor
                attributedString[mentionRange].link = URL(string: "\(Constants.baseUserTappedURLString)\(mention.handle)")
            }
        }

        // Format track timestamps
        if config.trackTimestampLinkEnabled {
            let timestamps = parseTimestamps(from: content)
            for timestamp in timestamps {
                guard timestamp.range.location >= 0 && timestamp.range.location + timestamp.range.length <= content.count else { continue }

                let startIndex = attributedString.index(attributedString.startIndex, offsetByCharacters: timestamp.range.location)
                let endIndex = attributedString.index(attributedString.startIndex, offsetByCharacters: min(timestamp.range.location + timestamp.range.length, attributedString.characters.count))
                let timestampRange = startIndex ..< endIndex

                // Format the timestamp
                attributedString[timestampRange].font = config.trackTimestampLinkFont.uiFont
                attributedString[timestampRange].foregroundColor = config.trackTimestampLinkColor
                attributedString[timestampRange].link = URL(string: "\(Constants.baseCommentTrackTimestampTappedURLString)\(timestamp.seconds)")
            }
        }
        return attributedString
    }
}

// MARK: - View Modifiers

public extension View {
    func onInteractiveTextTapped(
        userMention: @escaping (String) -> Void = { _ in },
        trackTimestamp: @escaping (Double) -> Void = { _ in }
    ) -> some View {
        modifier(InteractiveTextLinkViewModifier(
            onUserMentionTapped: userMention,
            onTrackTimestampTapped: trackTimestamp
        ))
    }

    func onUserMentionTapped(_ action: @escaping (String) -> Void) -> some View {
        onInteractiveTextTapped(userMention: action)
    }

    func onTrackTimestampTapped(_ action: @escaping (Double) -> Void) -> some View {
        onInteractiveTextTapped(trackTimestamp: action)
    }
}

private struct InteractiveTextLinkViewModifier: ViewModifier {
    let onUserMentionTapped: (String) -> Void
    let onTrackTimestampTapped: (Double) -> Void

    func body(content: Content) -> some View {
        content
            .environment(\.openURL, OpenURLAction { url in
                if url.scheme == "suno" {
                    if url.host() == "user-tapped" {
                        onUserMentionTapped(url.lastPathComponent)
                        return .handled
                    } else if url.host() == "track-timestamp-tapped" {
                        if let seconds = Double(url.lastPathComponent) {
                            onTrackTimestampTapped(seconds)
                        }
                        return .handled
                    }
                }

                return .systemAction
            })
    }
}
