package com.rjlresaka.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import javax.servlet.ServletContext;

import com.rjlresaka.model.ConversationItem;
import com.rjlresaka.model.User;
import com.rjlresaka.util.DatabaseConnection;

public class ConversationDAO {

    public Integer findConversationIdBetweenUsers(int firstUserId, int secondUserId, ServletContext context)
            throws SQLException, ClassNotFoundException {
        String sql = "SELECT c.id "
                + "FROM conversations c "
                + "INNER JOIN conversation_participants cp1 ON cp1.conversation_id = c.id AND cp1.user_id = ? "
                + "INNER JOIN conversation_participants cp2 ON cp2.conversation_id = c.id AND cp2.user_id = ? "
                + "WHERE c.is_group = 0 LIMIT 1";
        try (Connection connection = DatabaseConnection.getConnection(context);
             PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setInt(1, firstUserId);
            statement.setInt(2, secondUserId);
            try (ResultSet rs = statement.executeQuery()) {
                return rs.next() ? Integer.valueOf(rs.getInt("id")) : null;
            }
        }
    }

    public int findOrCreateConversationId(int firstUserId, int secondUserId, ServletContext context)
            throws SQLException, ClassNotFoundException {
        Integer existingConversationId = findConversationIdBetweenUsers(firstUserId, secondUserId, context);
        if (existingConversationId != null) {
            return existingConversationId.intValue();
        }

        String insertConversation = "INSERT INTO conversations (name, avatar_color, is_group, created_by) VALUES (NULL, '#E7F3FF', 0, ?)";
        String insertParticipant = "INSERT INTO conversation_participants (conversation_id, user_id, role) VALUES (?, ?, ?)";

        try (Connection connection = DatabaseConnection.getConnection(context)) {
            connection.setAutoCommit(false);
            try (PreparedStatement conversationStatement = connection.prepareStatement(insertConversation,
                         Statement.RETURN_GENERATED_KEYS);
                 PreparedStatement participantStatement = connection.prepareStatement(insertParticipant)) {

                conversationStatement.setInt(1, firstUserId);
                conversationStatement.executeUpdate();

                int conversationId = 0;
                try (ResultSet keys = conversationStatement.getGeneratedKeys()) {
                    if (keys.next()) {
                        conversationId = keys.getInt(1);
                    }
                }

                participantStatement.setInt(1, conversationId);
                participantStatement.setInt(2, firstUserId);
                participantStatement.setString(3, "admin");
                participantStatement.executeUpdate();

                participantStatement.setInt(1, conversationId);
                participantStatement.setInt(2, secondUserId);
                participantStatement.setString(3, "member");
                participantStatement.executeUpdate();

                connection.commit();
                return conversationId;
            } catch (SQLException exception) {
                connection.rollback();
                throw exception;
            } finally {
                connection.setAutoCommit(true);
            }
        }
    }

    public int createGroupConversation(int currentUserId, String name, List<Integer> memberIds, ServletContext context)
            throws SQLException, ClassNotFoundException {
        String groupName = cleanText(name);
        if (groupName == null) {
            groupName = "Nouveau groupe";
        }

        Set<Integer> uniqueMembers = new LinkedHashSet<Integer>();
        uniqueMembers.add(Integer.valueOf(currentUserId));
        if (memberIds != null) {
            for (Integer memberId : memberIds) {
                if (memberId != null && memberId.intValue() > 0) {
                    uniqueMembers.add(memberId);
                }
            }
        }

        String insertConversation = "INSERT INTO conversations (name, avatar_color, is_group, created_by) VALUES (?, '#E7F3FF', 1, ?)";
        String insertParticipant = "INSERT INTO conversation_participants (conversation_id, user_id, role) VALUES (?, ?, ?)";

        try (Connection connection = DatabaseConnection.getConnection(context)) {
            connection.setAutoCommit(false);
            try (PreparedStatement conversationStatement = connection.prepareStatement(insertConversation,
                         Statement.RETURN_GENERATED_KEYS);
                 PreparedStatement participantStatement = connection.prepareStatement(insertParticipant)) {

                conversationStatement.setString(1, groupName);
                conversationStatement.setInt(2, currentUserId);
                conversationStatement.executeUpdate();

                int conversationId = 0;
                try (ResultSet keys = conversationStatement.getGeneratedKeys()) {
                    if (keys.next()) {
                        conversationId = keys.getInt(1);
                    }
                }

                for (Integer memberId : uniqueMembers) {
                    participantStatement.setInt(1, conversationId);
                    participantStatement.setInt(2, memberId.intValue());
                    participantStatement.setString(3, memberId.intValue() == currentUserId ? "admin" : "member");
                    participantStatement.executeUpdate();
                }

                connection.commit();
                return conversationId;
            } catch (SQLException exception) {
                connection.rollback();
                throw exception;
            } finally {
                connection.setAutoCommit(true);
            }
        }
    }

    public List<ConversationItem> findConversationsForUser(int currentUserId, ServletContext context)
            throws SQLException, ClassNotFoundException {
        String sql = "SELECT c.id, c.name, c.avatar_color, c.is_group, c.updated_at, "
                + "other.id AS direct_user_id, other.full_name AS direct_name, other.username AS direct_username, "
                + "other.avatar_color AS direct_avatar_color, "
                + "(SELECT COUNT(*) FROM conversation_participants count_cp WHERE count_cp.conversation_id = c.id) AS member_count "
                + "FROM conversations c "
                + "INNER JOIN conversation_participants mine ON mine.conversation_id = c.id AND mine.user_id = ? "
                + "LEFT JOIN conversation_participants other_cp ON c.is_group = 0 AND other_cp.conversation_id = c.id AND other_cp.user_id <> ? "
                + "LEFT JOIN users other ON other.id = other_cp.user_id "
                + "ORDER BY c.updated_at DESC, c.created_at DESC";

        List<ConversationItem> conversations = new ArrayList<ConversationItem>();
        try (Connection connection = DatabaseConnection.getConnection(context);
             PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setInt(1, currentUserId);
            statement.setInt(2, currentUserId);
            try (ResultSet rs = statement.executeQuery()) {
                while (rs.next()) {
                    ConversationItem item = mapConversationBase(rs);
                    enrichConversationSnapshot(connection, item, currentUserId);
                    conversations.add(item);
                }
            }
        }
        return conversations;
    }

    public ConversationItem findConversationForUser(int conversationId, int currentUserId, ServletContext context)
            throws SQLException, ClassNotFoundException {
        String sql = "SELECT c.id, c.name, c.avatar_color, c.is_group, c.updated_at, "
                + "other.id AS direct_user_id, other.full_name AS direct_name, other.username AS direct_username, "
                + "other.avatar_color AS direct_avatar_color, "
                + "(SELECT COUNT(*) FROM conversation_participants count_cp WHERE count_cp.conversation_id = c.id) AS member_count "
                + "FROM conversations c "
                + "INNER JOIN conversation_participants mine ON mine.conversation_id = c.id AND mine.user_id = ? "
                + "LEFT JOIN conversation_participants other_cp ON c.is_group = 0 AND other_cp.conversation_id = c.id AND other_cp.user_id <> ? "
                + "LEFT JOIN users other ON other.id = other_cp.user_id "
                + "WHERE c.id = ? LIMIT 1";
        try (Connection connection = DatabaseConnection.getConnection(context);
             PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setInt(1, currentUserId);
            statement.setInt(2, currentUserId);
            statement.setInt(3, conversationId);
            try (ResultSet rs = statement.executeQuery()) {
                if (!rs.next()) {
                    return null;
                }
                ConversationItem item = mapConversationBase(rs);
                enrichConversationSnapshot(connection, item, currentUserId);
                return item;
            }
        }
    }

    public void enrichUsersWithConversationMeta(int currentUserId, List<User> users, ServletContext context)
            throws SQLException, ClassNotFoundException {
        for (User user : users) {
            Integer conversationId = findConversationIdBetweenUsers(currentUserId, user.getId(), context);
            user.setConversationId(conversationId);
            if (conversationId != null) {
                ConversationItem snapshot = findConversationForUser(conversationId.intValue(), currentUserId, context);
                if (snapshot != null) {
                    user.setLastMessagePreview(snapshot.getLastMessagePreview());
                    user.setLastMessageAt(snapshot.getLastMessageAt());
                    user.setUnreadCount(snapshot.getUnreadCount());
                }
            } else {
                user.setLastMessagePreview("Commencez la discussion");
            }
        }
    }

    public void markConversationAsSeen(int conversationId, int currentUserId, ServletContext context)
            throws SQLException, ClassNotFoundException {
        String sql = "UPDATE messages SET seen_at = NOW() WHERE conversation_id = ? AND sender_id <> ? AND seen_at IS NULL";
        try (Connection connection = DatabaseConnection.getConnection(context);
             PreparedStatement statement = connection.prepareStatement(sql)) {
            statement.setInt(1, conversationId);
            statement.setInt(2, currentUserId);
            statement.executeUpdate();
        }
    }

    private ConversationItem mapConversationBase(ResultSet rs) throws SQLException {
        ConversationItem item = new ConversationItem();
        item.setId(rs.getInt("id"));
        item.setGroup(rs.getInt("is_group") == 1);
        item.setMemberCount(rs.getInt("member_count"));
        item.setDirectUserId(rs.getObject("direct_user_id") == null ? null : Integer.valueOf(rs.getInt("direct_user_id")));

        if (item.isGroup()) {
            item.setTitle(defaultIfBlank(rs.getString("name"), "Groupe"));
            item.setAvatarColor(defaultIfBlank(rs.getString("avatar_color"), "#E7F3FF"));
            item.setSubtitle(item.getMemberCount() + " membres");
        } else {
            item.setTitle(defaultIfBlank(rs.getString("direct_name"), "Discussion privée"));
            item.setAvatarColor(defaultIfBlank(rs.getString("direct_avatar_color"), "#1877f2"));
            String username = rs.getString("direct_username");
            item.setSubtitle(username == null ? "Discussion privée" : "@" + username);
        }
        return item;
    }

    private void enrichConversationSnapshot(Connection connection, ConversationItem item, int currentUserId)
            throws SQLException {
        String lastMessageSql = "SELECT body, attachment_name, is_deleted, created_at FROM messages "
                + "WHERE conversation_id = ? ORDER BY created_at DESC, id DESC LIMIT 1";
        try (PreparedStatement statement = connection.prepareStatement(lastMessageSql)) {
            statement.setInt(1, item.getId());
            try (ResultSet rs = statement.executeQuery()) {
                if (rs.next()) {
                    item.setLastMessagePreview(formatPreview(rs.getString("body"), rs.getString("attachment_name"), rs.getBoolean("is_deleted")));
                    item.setLastMessageAt(rs.getTimestamp("created_at"));
                } else {
                    item.setLastMessagePreview(item.isGroup() ? "Nouveau groupe prêt à discuter" : "Dites bonjour 👋");
                    item.setLastMessageAt(null);
                }
            }
        }

        String unreadSql = "SELECT COUNT(*) AS total FROM messages WHERE conversation_id = ? AND sender_id <> ? AND seen_at IS NULL";
        try (PreparedStatement statement = connection.prepareStatement(unreadSql)) {
            statement.setInt(1, item.getId());
            statement.setInt(2, currentUserId);
            try (ResultSet rs = statement.executeQuery()) {
                if (rs.next()) {
                    item.setUnreadCount(rs.getInt("total"));
                }
            }
        }
    }

    private String formatPreview(String body, String attachmentName, boolean deleted) {
        if (deleted) {
            return "Message supprimé";
        }
        String cleanedBody = cleanText(body);
        if (cleanedBody != null) {
            return cleanedBody.length() > 54 ? cleanedBody.substring(0, 54) + "…" : cleanedBody;
        }
        if (attachmentName != null && !attachmentName.trim().isEmpty()) {
            return "📎 " + attachmentName;
        }
        return "Nouveau message";
    }

    private String defaultIfBlank(String value, String fallback) {
        return value == null || value.trim().isEmpty() ? fallback : value;
    }

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