using MySqlConnector;
using SharpChat.Messages;
using System.Text.Json;

namespace SharpChat.MariaDB;

public class MariaDBMessageStorage(MariaDBStorage storage) : MessageStorage {
    public async Task LogMessage(
        long id,
        string type,
        string channelName,
        string senderId,
        string senderName,
        ColourInheritable senderColour,
        int senderRank,
        string senderNick,
        UserPermissions senderPerms,
        object? data = null
    ) {
        try {
            using MariaDBConnection conn = await storage.CreateConnection();
            await conn.RunCommand(
                "INSERT INTO sqc_events (event_id, event_created, event_type, event_channel, event_data"
                + ", event_sender, event_sender_name, event_sender_colour, event_sender_rank, event_sender_nick, event_sender_perms)"
                + " VALUES (@id, NOW(), @type, @channel, @data"
                + ", @sender, @sender_name, @sender_colour, @sender_rank, @sender_nick, @sender_perms)",
                new MySqlParameter("id", id),
                new MySqlParameter("type", type),
                new MySqlParameter("channel", string.IsNullOrWhiteSpace(channelName) ? null : channelName),
                new MySqlParameter("data", data == null ? "{}" : JsonSerializer.SerializeToUtf8Bytes(data)),
                new MySqlParameter("sender", long.TryParse(senderId, out long senderId64) && senderId64 > 0 ? senderId64 : null),
                new MySqlParameter("sender_name", senderName),
                new MySqlParameter("sender_colour", senderColour.ToMisuzu()),
                new MySqlParameter("sender_rank", senderRank),
                new MySqlParameter("sender_nick", string.IsNullOrWhiteSpace(senderNick) ? null : senderNick),
                new MySqlParameter("sender_perms", MariaDBUserPermissionsConverter.To(senderPerms))
            );
        } catch(MySqlException ex) {
            Logger.Write(ex);
        }
    }

    public async Task DeleteMessage(Message msg) {
        try {
            using MariaDBConnection conn = await storage.CreateConnection();
            await conn.RunCommand(
                "UPDATE IGNORE sqc_events SET event_deleted = NOW() WHERE event_id = @id AND event_deleted IS NULL",
                new MySqlParameter("id", msg.Id)
            );
        } catch(MySqlException ex) {
            Logger.Write(ex);
        }
    }

    private static Message ReadMessage(MySqlDataReader reader) {
        using Stream data = reader.GetStream("event_data");
        return new Message(
            reader.GetInt64("event_id"),
            reader.GetString("event_type"),
            reader.IsDBNull(reader.GetOrdinal("event_sender")) ? null : reader.GetString("event_sender"),
            reader.IsDBNull(reader.GetOrdinal("event_sender_name")) ? string.Empty : reader.GetString("event_sender_name"),
            ColourInheritable.FromMisuzu(reader.GetInt32("event_sender_colour")),
            reader.GetInt32("event_sender_rank"),
            MariaDBUserPermissionsConverter.From((MariaDBUserPermissions)reader.GetInt32("event_sender_perms")),
            reader.IsDBNull(reader.GetOrdinal("event_sender_nick")) ? string.Empty : reader.GetString("event_sender_nick"),
            DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32("event_created")),
            reader.IsDBNull(reader.GetOrdinal("event_deleted")) ? null : DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32("event_deleted")),
            reader.IsDBNull(reader.GetOrdinal("event_channel")) ? null : reader.GetString("event_channel"),
            JsonDocument.Parse(data)
        );
    }

    public async Task<Message?> GetMessage(long id) {
        try {
            using MariaDBConnection conn = await storage.CreateConnection();
            using MySqlDataReader? reader = await conn.RunQuery(
                "SELECT event_id, event_type, event_data, event_channel"
                + ", event_sender, event_sender_name, event_sender_colour, event_sender_rank, event_sender_nick, event_sender_perms"
                + ", UNIX_TIMESTAMP(event_created) AS event_created"
                + ", UNIX_TIMESTAMP(event_deleted) AS event_deleted"
                + " FROM sqc_events"
                + " WHERE event_id = @id",
                new MySqlParameter("id", id)
            );

            if(reader is null)
                return null;

            while(reader.Read()) {
                Message evt = ReadMessage(reader);
                if(evt != null)
                    return evt;
            }
        } catch(MySqlException ex) {
            Logger.Write(ex);
        }

        return null;
    }

    public async Task<IEnumerable<Message>> GetMessages(string channelName, int amount = 20, int offset = 0) {
        List<Message> msgs = [];

        try {
            using MariaDBConnection conn = await storage.CreateConnection();
            using MySqlDataReader? reader = await conn.RunQuery(
                "SELECT event_id, event_type, event_data, event_channel"
                + ", event_sender, event_sender_name, event_sender_colour, event_sender_rank, event_sender_nick, event_sender_perms"
                + ", UNIX_TIMESTAMP(event_created) AS event_created"
                + ", UNIX_TIMESTAMP(event_deleted) AS event_deleted"
                + " FROM sqc_events"
                + " WHERE event_deleted IS NULL AND (event_channel = @channel OR event_channel IS NULL)"
                + " AND event_id > @offset"
                + " ORDER BY event_id DESC"
                + " LIMIT @amount",
                new MySqlParameter("channel", channelName),
                new MySqlParameter("amount", amount),
                new MySqlParameter("offset", offset)
            );
            if(reader is null)
                return msgs;

            while(reader.Read()) {
                Message evt = ReadMessage(reader);
                if(evt != null)
                    msgs.Add(evt);
            }
        } catch(MySqlException ex) {
            Logger.Write(ex);
        }

        msgs.Reverse();

        return msgs;
    }
}