import Foundation
import GenAPI

public struct ClipComment: Codable, Equatable, Hashable, Identifiable {
    enum CodingKeys: String, CodingKey {
        case id
        case clipID = "clip_id"
        case userID = "user_id"
        case userDisplayName = "user_display_name"
        case userAvatarUrl = "user_avatar_url"
        case userHandle = "user_handle"
        case content
        case createdAt = "created_at"
        case numLikes = "num_likes"
        case numReports = "num_reports"
        case parentID = "parent_id"
        case trackTimestamp = "track_timestamp"
        case reactionType = "reaction_type"
        case replies
        case numReplies = "num_replies"
        case replyContinuationToken = "reply_continuation_token"
        case userMentions = "user_mentions"
    }

    public let id: String
    public var clipID: String
    public let userID: String
    public let userDisplayName: String
    public let userAvatarUrl: String
    public let userHandle: String
    public let content: String
    public let createdAt: Date
    public var numLikes: Int
    public let numReports: Int
    public var parentID: String?
    public let trackTimestamp: TimeInterval
    public var reactionType: String?
    public var numReplies: Int
    public var replyContinuationToken: String?
    public let userMentions: [CommentUserMention]

    public var reaction: CommentReactionType {
        return .init(reactionType)
    }

    public var isReply: Bool {
        return parentID != nil
    }

    public var replies: [ClipComment]

    public init(
        id: String,
        clipID: String,
        userID: String,
        userDisplayName: String,
        userAvatarUrl: String,
        userHandle: String,
        content: String,
        createdAt: Date,
        numLikes: Int,
        numReports: Int,
        parentID: String?,
        trackTimestamp: Double,
        reactionType: String?,
        replyContinuationToken _: String?,
        replies: [ClipComment] = [],
        numReplies: Int,
        userMentions: [CommentUserMention] = []
    ) {
        self.id = id
        self.clipID = clipID
        self.userID = userID
        self.userDisplayName = userDisplayName
        self.userAvatarUrl = userAvatarUrl
        self.userHandle = userHandle
        self.content = content
        self.createdAt = createdAt
        self.numLikes = numLikes
        self.numReports = numReports
        self.parentID = parentID
        self.trackTimestamp = trackTimestamp
        self.reactionType = reactionType
        self.replies = replies
        self.numReplies = numReplies
        self.userMentions = userMentions
    }

    init(_ remote: Components.Schemas.Comment) throws {
        id = remote.id

        /*
            In getCommentsForClip(...)
            Only reply comments will have no clip_id
         */

        clipID = remote.clip_id ?? ""
        userID = remote.user_id
        userDisplayName = remote.user_display_name ?? ""
        userAvatarUrl = remote.user_avatar_url ?? ""
        userHandle = remote.user_handle ?? ""
        content = remote.content
        createdAt = remote.created_at
        numLikes = remote.num_likes ?? .zero
        numReports = remote.num_reports ?? .zero
        parentID = remote.parent_id /* Only replies have parent_id */
        trackTimestamp = TimeInterval(remote.track_timestamp ?? .zero)
        reactionType = remote.reaction_type
        let schemaReplies: [Components.Schemas.Comment] = remote.replies ?? []
        replies = schemaReplies.compactMap { try? ClipComment($0) }
        replyContinuationToken = remote.reply_continuation_token
        numReplies = remote.num_replies ?? .zero
        let schemaUserMentions: [Components.Schemas.CommentUserMention] = remote.user_mentions ?? []
        userMentions = schemaUserMentions.compactMap { try? CommentUserMention($0) }

        for index in replies.indices {
            replies[index].parentID = id
            replies[index].clipID = clipID
        }
    }
}

public extension ClipComment {
    static func fromAPIV2(_ apiV2Comment: CommentSchema) -> ClipComment {
        .init(
            id: apiV2Comment.id,
            clipID: apiV2Comment.clipId ?? "",
            userID: apiV2Comment.userId,
            userDisplayName: apiV2Comment.userDisplayName,
            userAvatarUrl: apiV2Comment.userAvatarUrl,
            userHandle: apiV2Comment.userHandle,
            content: apiV2Comment.content,
            createdAt: apiV2Comment.createdAt,
            numLikes: apiV2Comment.numLikes,
            numReports: 0,
            parentID: apiV2Comment.parentId,
            trackTimestamp: apiV2Comment.trackTimestamp ?? 0,
            reactionType: apiV2Comment.reactionType,
            replyContinuationToken: apiV2Comment.replyContinuationToken,
            replies: apiV2Comment.replies?.map { ClipComment.fromReplyAPIV2($0) } ?? [],
            numReplies: apiV2Comment.numReplies ?? 0,
            userMentions: apiV2Comment.userMentions?.map { .init(start: $0.start, end: $0.end, handle: $0.handle, displayName: $0.displayName) } ?? []
        )
    }

    static func fromReplyAPIV2(_ apiV2Reply: ReplySchema) -> ClipComment {
        .init(
            id: apiV2Reply.id,
            /// Only reply comments will have no `clip_id`
            clipID: "",
            userID: apiV2Reply.userId,
            userDisplayName: apiV2Reply.userDisplayName,
            userAvatarUrl: apiV2Reply.userAvatarUrl,
            userHandle: apiV2Reply.userHandle,
            content: apiV2Reply.content,
            createdAt: apiV2Reply.createdAt,
            numLikes: apiV2Reply.numLikes,
            numReports: 0,
            parentID: apiV2Reply.parentId,
            trackTimestamp: 0,
            reactionType: apiV2Reply.reactionType,
            replyContinuationToken: nil,
            replies: [],
            numReplies: 0,
            userMentions: apiV2Reply.userMentions?.map { .init(start: $0.start, end: $0.end, handle: $0.handle, displayName: $0.displayName) } ?? []
        )
    }
}

#if DEBUG
    public extension ClipComment {
        static func mock() -> ClipComment {
            ClipComment(
                id: UUID().uuidString,
                clipID: "clip123",
                userID: "user456",
                userDisplayName: "John Smith",
                userAvatarUrl: "https://example.com/avatar.jpg",
                userHandle: "@johnsmith",
                content: "This is a mock comment",
                createdAt: Date(),
                numLikes: 42,
                numReports: 0,
                parentID: nil,
                trackTimestamp: 123.45,
                reactionType: "like",
                replyContinuationToken: nil,
                numReplies: 5
            )
        }
    }
#endif
