package com.rjlresaka.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletContext;

import com.rjlresaka.model.Message;
import com.rjlresaka.model.ReactionStat;
import com.rjlresaka.util.DatabaseConnection;

public class MessageDAO {
    private final ConversationDAO conversationDAO = new ConversationDAO();

    public List<Message> findMessagesBetweenUsers(int currentUserId, int otherUserId, ServletContext context)
            throws SQLException, ClassNotFoundException {
        Integer conversationId = conversationDAO.findConversationIdBetweenUsers(currentUserId, otherUserId, context);
        if (conversationId == null) {
            return new ArrayList<Message>();
        }
        return findMessagesByConversation(conversationId.intValue(), currentUserId, context);
    }

    public List<Message> findMessagesByConversation(int conversationId, int currentUserId, ServletContext context)
            throws SQLException, ClassNotFoundException {
        String sql = "SELECT m.*, u.full_name, u.avatar_color "
                + "FROM messages m "
                + "INNER JOIN users u ON u.id = m.sender_id "
                + "INNER JOIN conversation_participants cp ON cp.conversation_id = m.conversation_id AND cp.user_id = ? "
                + "WHERE m.conversation_id = ? "
                + "ORDER BY m.created_at ASC, m.id ASC";
        List<Message> messages = new ArrayList<Message>();
        try (Connection connection = DatabaseConnection.getConnection(context);
             PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setInt(1, currentUserId);
            statement.setInt(2, conversationId);
            try (ResultSet rs = statement.executeQuery()) {
                while (rs.next()) {
                    Message message = mapMessage(rs, currentUserId);
                    message.getReactions().addAll(findReactions(message.getId(), currentUserId, context));
                    messages.add(message);
                }
            }
        }
        return messages;
    }

    public int createMessage(int senderId, int receiverId, Message draft, ServletContext context)
            throws SQLException, ClassNotFoundException {
        int conversationId = conversationDAO.findOrCreateConversationId(senderId, receiverId, context);
        createMessageInConversation(senderId, conversationId, draft, context);
        return conversationId;
    }

    public void createMessageInConversation(int senderId, int conversationId, Message draft, ServletContext context)
            throws SQLException, ClassNotFoundException {
        String sql = "INSERT INTO messages (conversation_id, sender_id, body, attachment_name, attachment_path, attachment_type, attachment_size) "
                + "VALUES (?, ?, ?, ?, ?, ?, ?)";
        String updateConversation = "UPDATE conversations SET updated_at = NOW() WHERE id = ?";

        try (Connection connection = DatabaseConnection.getConnection(context)) {
            connection.setAutoCommit(false);
            try {
                if (!hasAccess(connection, conversationId, senderId)) {
                    throw new SQLException("Accès refusé à cette conversation.");
                }
                try (PreparedStatement statement = connection.prepareStatement(sql)) {
                    statement.setInt(1, conversationId);
                    statement.setInt(2, senderId);
                    statement.setString(3, cleanText(draft.getBody()));
                    statement.setString(4, emptyToNull(draft.getAttachmentName()));
                    statement.setString(5, emptyToNull(draft.getAttachmentPath()));
                    statement.setString(6, emptyToNull(draft.getAttachmentType()));
                    if (draft.getAttachmentSize() > 0) {
                        statement.setLong(7, draft.getAttachmentSize());
                    } else {
                        statement.setNull(7, java.sql.Types.BIGINT);
                    }
                    statement.executeUpdate();
                }
                try (PreparedStatement statement = connection.prepareStatement(updateConversation)) {
                    statement.setInt(1, conversationId);
                    statement.executeUpdate();
                }
                connection.commit();
            } catch (SQLException exception) {
                connection.rollback();
                throw exception;
            } finally {
                connection.setAutoCommit(true);
            }
        }
    }

    public boolean updateMessage(int messageId, int senderId, String body, ServletContext context)
            throws SQLException, ClassNotFoundException {
        String sql = "UPDATE messages SET body = ?, is_edited = 1, updated_at = NOW() "
                + "WHERE id = ? AND sender_id = ? AND is_deleted = 0";
        try (Connection connection = DatabaseConnection.getConnection(context);
             PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setString(1, cleanText(body));
            statement.setInt(2, messageId);
            statement.setInt(3, senderId);
            return statement.executeUpdate() > 0;
        }
    }

    public boolean deleteMessage(int messageId, int senderId, ServletContext context)
            throws SQLException, ClassNotFoundException {
        String sql = "UPDATE messages SET body = NULL, attachment_name = NULL, attachment_path = NULL, attachment_type = NULL, "
                + "attachment_size = NULL, is_deleted = 1, updated_at = NOW() WHERE id = ? AND sender_id = ?";
        try (Connection connection = DatabaseConnection.getConnection(context);
             PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setInt(1, messageId);
            statement.setInt(2, senderId);
            return statement.executeUpdate() > 0;
        }
    }

    public void toggleReaction(int messageId, int userId, String emoji, ServletContext context)
            throws SQLException, ClassNotFoundException {
        if (emoji == null || emoji.trim().isEmpty()) {
            return;
        }
        String select = "SELECT id FROM message_reactions WHERE message_id = ? AND user_id = ? AND emoji = ? LIMIT 1";
        String insert = "INSERT INTO message_reactions (message_id, user_id, emoji) VALUES (?, ?, ?)";
        String delete = "DELETE FROM message_reactions WHERE id = ?";

        try (Connection connection = DatabaseConnection.getConnection(context);
             PreparedStatement selectStatement = connection.prepareStatement(select)) {
            selectStatement.setInt(1, messageId);
            selectStatement.setInt(2, userId);
            selectStatement.setString(3, emoji);
            try (ResultSet rs = selectStatement.executeQuery()) {
                if (rs.next()) {
                    try (PreparedStatement deleteStatement = connection.prepareStatement(delete)) {
                        deleteStatement.setInt(1, rs.getInt("id"));
                        deleteStatement.executeUpdate();
                    }
                } else {
                    try (PreparedStatement insertStatement = connection.prepareStatement(insert)) {
                        insertStatement.setInt(1, messageId);
                        insertStatement.setInt(2, userId);
                        insertStatement.setString(3, emoji);
                        insertStatement.executeUpdate();
                    }
                }
            }
        }
    }

    public Message findAttachmentForDownload(int messageId, int currentUserId, ServletContext context)
            throws SQLException, ClassNotFoundException {
        String sql = "SELECT m.*, u.full_name, u.avatar_color "
                + "FROM messages m "
                + "INNER JOIN users u ON u.id = m.sender_id "
                + "INNER JOIN conversation_participants cp ON cp.conversation_id = m.conversation_id AND cp.user_id = ? "
                + "WHERE m.id = ? LIMIT 1";
        try (Connection connection = DatabaseConnection.getConnection(context);
             PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setInt(1, currentUserId);
            statement.setInt(2, messageId);
            try (ResultSet rs = statement.executeQuery()) {
                return rs.next() ? mapMessage(rs, currentUserId) : null;
            }
        }
    }

    private boolean hasAccess(Connection connection, int conversationId, int userId) throws SQLException {
        String sql = "SELECT 1 FROM conversation_participants WHERE conversation_id = ? AND user_id = ? LIMIT 1";
        try (PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setInt(1, conversationId);
            statement.setInt(2, userId);
            try (ResultSet rs = statement.executeQuery()) {
                return rs.next();
            }
        }
    }

    private List<ReactionStat> findReactions(int messageId, int currentUserId, ServletContext context)
            throws SQLException, ClassNotFoundException {
        String sql = "SELECT emoji, COUNT(*) AS total, MAX(CASE WHEN user_id = ? THEN 1 ELSE 0 END) AS mine "
                + "FROM message_reactions WHERE message_id = ? GROUP BY emoji ORDER BY total DESC, emoji ASC";
        List<ReactionStat> reactions = new ArrayList<ReactionStat>();
        try (Connection connection = DatabaseConnection.getConnection(context);
             PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setInt(1, currentUserId);
            statement.setInt(2, messageId);
            try (ResultSet rs = statement.executeQuery()) {
                while (rs.next()) {
                    ReactionStat stat = new ReactionStat();
                    stat.setEmoji(rs.getString("emoji"));
                    stat.setCount(rs.getInt("total"));
                    stat.setReactedByCurrentUser(rs.getInt("mine") == 1);
                    reactions.add(stat);
                }
            }
        }
        return reactions;
    }

    private Message mapMessage(ResultSet rs, int currentUserId) throws SQLException {
        Message message = new Message();
        message.setId(rs.getInt("id"));
        message.setConversationId(rs.getInt("conversation_id"));
        message.setSenderId(rs.getInt("sender_id"));
        message.setSenderName(rs.getString("full_name"));
        message.setSenderAvatarColor(rs.getString("avatar_color"));
        message.setSenderInitials(initials(rs.getString("full_name")));
        message.setBody(rs.getString("body"));
        message.setAttachmentName(rs.getString("attachment_name"));
        message.setAttachmentPath(rs.getString("attachment_path"));
        message.setAttachmentType(rs.getString("attachment_type"));
        message.setAttachmentSize(rs.getLong("attachment_size"));
        message.setEdited(rs.getBoolean("is_edited"));
        message.setDeleted(rs.getBoolean("is_deleted"));
        message.setMine(rs.getInt("sender_id") == currentUserId);
        message.setCreatedAt(rs.getTimestamp("created_at"));
        message.setUpdatedAt(rs.getTimestamp("updated_at"));
        return message;
    }

    private String initials(String fullName) {
        if (fullName == null || fullName.trim().isEmpty()) {
            return "RR";
        }
        String[] parts = fullName.trim().split("\\s+");
        StringBuilder builder = new StringBuilder();
        for (String part : parts) {
            if (!part.isEmpty()) {
                builder.append(Character.toUpperCase(part.charAt(0)));
            }
            if (builder.length() == 2) {
                break;
            }
        }
        return builder.length() == 0 ? "RR" : builder.toString();
    }

    private String cleanText(String body) {
        if (body == null) {
            return null;
        }
        String cleaned = body.trim();
        return cleaned.isEmpty() ? null : cleaned;
    }

    private String emptyToNull(String value) {
        if (value == null || value.trim().isEmpty()) {
            return null;
        }
        return value;
    }
}
