import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

/// https://discord.com/developers/docs/topics/gateway#gateway-intents
public struct Intents: OptionSet, Sendable {
    public var rawValue: UInt32

    public static let guilds = Intents(rawValue: 1 << 0)
    public static let guildMembers = Intents(rawValue: 1 << 1)
    public static let guildModeration = Intents(rawValue: 1 << 2)
    public static let guildEmojisAndStickers = Intents(rawValue: 1 << 3)
    public static let guildIntegrations = Intents(rawValue: 1 << 4)
    public static let guildWebhooks = Intents(rawValue: 1 << 5)
    public static let guildInvites = Intents(rawValue: 1 << 6)
    public static let guildVoiceStates = Intents(rawValue: 1 << 7)
    public static let guildPresences = Intents(rawValue: 1 << 8)
    public static let guildMessages = Intents(rawValue: 1 << 9)
    public static let guildMessageReactions = Intents(rawValue: 1 << 10)
    public static let guildMessageTyping = Intents(rawValue: 1 << 11)
    public static let directMessages = Intents(rawValue: 1 << 12)
    public static let directMessageReactions = Intents(rawValue: 1 << 13)
    public static let directMessageTyping = Intents(rawValue: 1 << 14)
    public static let messageContent = Intents(rawValue: 1 << 15)
    public static let guildScheduledEvents = Intents(rawValue: 1 << 16)
    public static let autoModerationConfiguration = Intents(rawValue: 1 << 20)
    public static let autoModerationExecution = Intents(rawValue: 1 << 21)
    public static let guildMessagePolls = Intents(rawValue: 1 << 24)
    public static let directMessagePolls = Intents(rawValue: 1 << 25)

    public init(rawValue: UInt32) {
        self.rawValue = rawValue
    }
}

public struct GetGatewayResponse: Codable, Sendable {
    public let url: URL
    public let shards: Int
    public let session_start_limit: SessionStartLimit
}

public struct SessionStartLimit: Codable, Sendable {
    public let total: Int
    public let remaining: Int
    public let reset_after: Int
    public let max_concurrency: Int
}

public enum GatewayPayload: Decodable, Sendable, Equatable {
    case hello(GatewayHello)
    case messageCreate(MessageCreate)
    case ready(GatewayReady)
    case heartbeatAck
}

public struct GatewayMessage: Decodable, Sendable {
    public let op: Int
    public let d: GatewayPayload?
    public let s: Int?
    public let t: String?

    enum CodingKeys: String, CodingKey {
        case t
        case s
        case op
        case d
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        op = try container.decode(Int.self, forKey: .op)
        s = try container.decode(Int?.self, forKey: .s)
        t = try container.decode(String?.self, forKey: .t)
        switch op {
            case 11:
                d = .heartbeatAck
            case 10:
                let hello = try container.decode(GatewayHello.self, forKey: .d)
                d = .hello(hello)
            case 0 where t == "MESSAGE_CREATE":
                let messageCreate = try container.decode(MessageCreate.self, forKey: .d)
                d = .messageCreate(messageCreate)
            case 0 where t == "READY":
                let ready = try container.decode(GatewayReady.self, forKey: .d)
                d = .ready(ready)
            default:
                d = nil
                break
        }
    }

    public init(op: Int, d: GatewayPayload?, s: Int?, t: String?) {
        self.op = op
        self.d = d
        self.s = s
        self.t = t
    }
}

public struct GatewayReady: Codable, Sendable, Equatable {
    public let session_id: String
    public let resume_gateway_url: URL
}

public struct GatewayHello: Codable, Sendable, Equatable {
    public let heartbeat_interval: Int
}

public struct CreateMessageReq: Codable, Sendable {
    public init(content: String? = nil, message_reference: MessageRefrence? = nil, embeds: [Embed]? = nil, files: [RawFile]? = nil, attachments: [Attachment]? = nil) {
        self.content = content
        self.message_reference = message_reference
        self.embeds = embeds
        self.files = files
        self.attachments = attachments
    }

    public let content: String?
    public let message_reference: MessageRefrence?
    public let embeds: [Embed]?
    public let files: [RawFile]?
    public let attachments: [Attachment]?
}

public struct Attachment: Codable, Sendable {
    public init(index: Int, filename: String? = nil) {
        self.index = index
        self.filename = filename
    }

    public let index: Int
    public let filename: String?
}

public struct RawFile: Codable, Sendable {
    public init(data: Data, filename: String? = nil) {
        self.data = data
        self.filename = filename
    }

    public let data: Data
    public let filename: String?
}

public struct Embed: Codable, Sendable {
    public init(description: String? = nil, image: EmbedImage? = nil) {
        self.description = description
        self.image = image
    }

    public let description: String?
    public let image: EmbedImage?
}

public struct EmbedImage: Codable, Sendable {
    public init(url: String) {
        self.url = url
    }

    public let url: String
}

public struct MessageRefrence: Codable, Sendable {
    public init(type: Int? = nil, message_id: String? = nil, channel_id: String? = nil, guild_id: String? = nil) {
        self.type = type
        self.message_id = message_id
        self.channel_id = channel_id
        self.guild_id = guild_id
    }

    let type: Int?
    let message_id: String?
    let channel_id: String?
    let guild_id: String?
}

public struct MessageCreate: Codable, Sendable, Equatable {
    public let id: String
    public let channel_id: String
    public let guild_id: String?
    public let author: User?
    public let content: String
    public let mentions: [MentionUser]
    public let member: GuildMember?
}


public struct User: Codable, Sendable, Equatable {
    public let id: String?
    public let bot: Bool?
    public let global_name: String?
    public let username: String
}

public struct GuildMember: Codable, Sendable, Equatable {
    public let user: User?
    public let nick: String?
}

public struct MentionUser: Codable, Sendable, Equatable {
    public let id: String?
    public let bot: Bool?
    public let global_name: String?
    public let username: String
    public let member: GuildMember?
}

