import Foundation
import GRDB

/// SQLite record for the `messages` table.
/// Android parity: `MessageEntity` in `MessageEntities.kt`.
struct MessageRecord: Codable, FetchableRecord, MutablePersistableRecord, Sendable {
    static let databaseTableName = "messages"

    var id: Int64?
    var account: String
    var fromPublicKey: String
    var toPublicKey: String
    var content: String
    var chachaKey: String
    var text: String
    var plainMessage: String
    var timestamp: Int64
    var isRead: Int
    var readAlias: Int
    var fromMe: Int
    var deliveryStatus: Int
    var deliveredAlias: Int
    var messageId: String
    var replyToMessageId: String?
    var dialogKey: String
    var attachments: String
    var attachmentPassword: String?

    // MARK: - Column mapping

    enum Columns: String, ColumnExpression {
        case id, account
        case fromPublicKey = "from_public_key"
        case toPublicKey = "to_public_key"
        case content
        case chachaKey = "chacha_key"
        case text, timestamp
        case plainMessage = "plain_message"
        case isRead = "is_read"
        case readAlias = "read"
        case fromMe = "from_me"
        case deliveryStatus = "delivery_status"
        case deliveredAlias = "delivered"
        case messageId = "message_id"
        case replyToMessageId = "reply_to_message_id"
        case dialogKey = "dialog_key"
        case attachments
        case attachmentPassword = "attachment_password"
    }

    enum CodingKeys: String, CodingKey {
        case id, account
        case fromPublicKey = "from_public_key"
        case toPublicKey = "to_public_key"
        case content
        case chachaKey = "chacha_key"
        case text, timestamp
        case plainMessage = "plain_message"
        case isRead = "is_read"
        case readAlias = "read"
        case fromMe = "from_me"
        case deliveryStatus = "delivery_status"
        case deliveredAlias = "delivered"
        case messageId = "message_id"
        case replyToMessageId = "reply_to_message_id"
        case dialogKey = "dialog_key"
        case attachments
        case attachmentPassword = "attachment_password"
    }

    // MARK: - Auto-increment

    mutating func didInsert(_ inserted: InsertionSuccess) {
        id = inserted.rowID
    }

    // MARK: - Conversions

    func toChatMessage(overrideText: String? = nil) -> ChatMessage {
        let decoder = JSONDecoder()
        let parsedAttachments: [MessageAttachment]
        if let data = attachments.data(using: .utf8),
           let decoded = try? decoder.decode([MessageAttachment].self, from: data) {
            parsedAttachments = decoded
        } else {
            parsedAttachments = []
        }

        return ChatMessage(
            id: messageId,
            fromPublicKey: fromPublicKey,
            toPublicKey: toPublicKey,
            text: overrideText ?? (plainMessage.isEmpty ? text : plainMessage),
            timestamp: timestamp,
            deliveryStatus: DeliveryStatus(rawValue: deliveryStatus) ?? .waiting,
            isRead: (isRead != 0) || (readAlias != 0),
            attachments: parsedAttachments,
            attachmentPassword: attachmentPassword
        )
    }

    static func from(_ message: ChatMessage, account: String) -> MessageRecord {
        let encoder = JSONEncoder()
        let attachmentsJSON: String
        if let data = try? encoder.encode(message.attachments),
           let str = String(data: data, encoding: .utf8) {
            attachmentsJSON = str
        } else {
            attachmentsJSON = "[]"
        }

        let fromMe = message.fromPublicKey == account
        return MessageRecord(
            id: nil,
            account: account,
            fromPublicKey: message.fromPublicKey,
            toPublicKey: message.toPublicKey,
            content: "",
            chachaKey: "",
            text: message.text,
            plainMessage: message.text,
            timestamp: message.timestamp,
            isRead: message.isRead ? 1 : 0,
            readAlias: message.isRead ? 1 : 0,
            fromMe: fromMe ? 1 : 0,
            deliveryStatus: message.deliveryStatus.rawValue,
            deliveredAlias: message.deliveryStatus.rawValue,
            messageId: message.id,
            replyToMessageId: nil,
            dialogKey: DatabaseManager.dialogKey(account: account, opponentKey: fromMe ? message.toPublicKey : message.fromPublicKey),
            attachments: attachmentsJSON,
            attachmentPassword: message.attachmentPassword
        )
    }
}
