package com.reucon.openfire.plugin.archive.impl;

import com.reucon.openfire.plugin.archive.ArchivedMessageConsumer;
import com.reucon.openfire.plugin.archive.PersistenceManager;
import com.reucon.openfire.plugin.archive.model.ArchivedMessage;
import com.reucon.openfire.plugin.archive.model.Conversation;
import com.reucon.openfire.plugin.archive.model.Participant;
import com.reucon.openfire.plugin.archive.xep0059.XmppResultSet;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.muc.MUCRoom;
import org.jivesoftware.openfire.muc.MultiUserChatManager;
import org.jivesoftware.openfire.muc.MultiUserChatService;
import org.jivesoftware.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

/**
 * Created by dwd on 25/07/16.
 */
public class MucMamPersistenceManager implements PersistenceManager {
    private final static Logger Log = LoggerFactory.getLogger( MucMamPersistenceManager.class );
    private static final String LOAD_HISTORY =
            "SELECT sender, nickname, logTime, subject, body, stanza, messageId FROM ofMucConversationLog " +
                    "WHERE messageId IS NOT NULL AND logTime>? AND logTime <= ? AND roomID=? AND (nickname IS NOT NULL OR subject IS NOT NULL) ";
    private static final String WHERE_SENDER = " AND sender = ? ";
    private static final String WHERE_AFTER = " AND messageId > ? ";
    private static final String WHERE_BEFORE = " AND messageId < ? ";
    private static final String ORDER_BY = " ORDER BY logTime";
    @Override
    public boolean createMessage(ArchivedMessage message) {
        throw new UnsupportedOperationException("MAM-MUC cannot perform this operation");
    }

    @Override
    public int processAllMessages(ArchivedMessageConsumer callback) {
        throw new UnsupportedOperationException("MAM-MUC cannot perform this operation");
    }

    @Override
    public boolean createConversation(Conversation conversation) {
        throw new UnsupportedOperationException("MAM-MUC cannot perform this operation");
    }

    @Override
    public boolean updateConversationEnd(Conversation conversation) {
        throw new UnsupportedOperationException("MAM-MUC cannot perform this operation");
    }

    @Override
    public boolean createParticipant(Participant participant, Long conversationId) {
        throw new UnsupportedOperationException("MAM-MUC cannot perform this operation");
    }

    @Override
    public List<Conversation> findConversations(String[] participants, Date startDate, Date endDate) {
        throw new UnsupportedOperationException("MAM-MUC cannot perform this operation");
    }

    @Override
    public Collection<Conversation> findConversations(Date startDate, Date endDate, String owner, String with, XmppResultSet xmppResultSet) {
        throw new UnsupportedOperationException("MAM-MUC cannot perform this operation");
    }

    @Override
    public Collection<ArchivedMessage> findMessages(Date startDate, Date endDate, String owner, String with, XmppResultSet xmppResultSet) {
        Log.debug( "Finding messages of owner '{}' with start date '{}', end date '{}' with '{}' and resultset '{}'.", new Object[] { owner, startDate, endDate, with, xmppResultSet } );
        JID mucRoom = new JID(owner);
        MultiUserChatManager manager = XMPPServer.getInstance().getMultiUserChatManager();
        MultiUserChatService service =  manager.getMultiUserChatService(mucRoom);
        MUCRoom room = service.getChatRoom(mucRoom.getNode());
        Connection connection = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        // If logging isn't enabled, do nothing.
        if (!room.isLogEnabled()) return null;
        List<ArchivedMessage>msgs = new LinkedList<>();
        if (startDate == null) {
            startDate = new Date(0L);
        }
        if (endDate == null) {
            endDate = new Date();
        }
        int max = xmppResultSet.getMax();
        with = null; // TODO: Suppress this, since we don't yet have requestor information for access control.
        try {
            connection = DbConnectionManager.getConnection();
            StringBuilder sql = new StringBuilder(LOAD_HISTORY);
            if (with != null) {
                sql.append(WHERE_SENDER);
            }
            if (xmppResultSet.getAfter() != null) {
                sql.append(WHERE_AFTER);
            }
            if (xmppResultSet.getBefore() != null) {
                sql.append(WHERE_BEFORE);
            }
            sql.append(ORDER_BY);
            pstmt = connection.prepareStatement(sql.toString());
            pstmt.setString(1, StringUtils.dateToMillis(startDate));
            pstmt.setString(2, StringUtils.dateToMillis(endDate));
            pstmt.setLong(3, room.getID());
            int pos = 3;
            if (with != null) {
                pstmt.setString(++pos, with);
            }
            if (xmppResultSet.getAfter() != null) {
                pstmt.setLong(++pos, xmppResultSet.getAfter());
            }
            if (xmppResultSet.getBefore() != null) {
                pstmt.setLong(++pos, xmppResultSet.getBefore());
            }
            rs = pstmt.executeQuery();
            while (rs.next()) {
                String senderJID = rs.getString(1);
                String nickname = rs.getString(2);
                Date sentDate = new Date(Long.parseLong(rs.getString(3).trim()));
                String subject = rs.getString(4);
                String body = rs.getString(5);
                String stanza = rs.getString(6);
                long id = rs.getLong(7);
                if (stanza == null) {
                    Message message = new Message();
                    message.setType(Message.Type.groupchat);
                    message.setSubject(subject);
                    message.setBody(body);
                    // Set the sender of the message
                    if (nickname != null && nickname.trim().length() > 0) {
                        JID roomJID = room.getRole().getRoleAddress();
                        // Recreate the sender address based on the nickname and room's JID
                        message.setFrom(new JID(roomJID.getNode(), roomJID.getDomain(), nickname, true));
                    }
                    else {
                        // Set the room as the sender of the message
                        message.setFrom(room.getRole().getRoleAddress());
                    }
                    stanza = message.toString();
                }
                ArchivedMessage archivedMessage = new ArchivedMessage(sentDate, ArchivedMessage.Direction.from, null, null);
                archivedMessage.setStanza(stanza);
                archivedMessage.setId(id);
                msgs.add(archivedMessage);
            }
        } catch (SQLException e) {
            Log.error("SQL failure during MAM-MUC: ", e);
        } finally {
            DbConnectionManager.closeConnection(rs, pstmt, connection);
        }
        // TODO - Not great, really should be done by suitable LIMIT stuff.
        // Would need to reverse ordering in some cases and then reverse results.
        boolean pagingBackwards = xmppResultSet.getBefore() != null && xmppResultSet.getAfter() == null;
        if ( pagingBackwards ) {
            Collections.reverse(msgs);
        }
        boolean complete = true;
        xmppResultSet.setCount(msgs.size());
        while (msgs.size() > max) {
            msgs.remove(msgs.size() - 1);
            complete = false;
        }
        if ( pagingBackwards ) {
            Collections.reverse(msgs);
        }
        xmppResultSet.setComplete(complete);
        if (msgs.size() > 0) {
            xmppResultSet.setFirst(msgs.get(0).getId());
            if (msgs.size() > 1) {
                xmppResultSet.setLast(msgs.get(msgs.size() - 1).getId());
            }
        }
        return msgs;
    }

    @Override
    public Collection<Conversation> getActiveConversations(int conversationTimeout) {
        throw new UnsupportedOperationException("MAM-MUC cannot perform this operation");
    }

    @Override
    public List<Conversation> getConversations(Collection<Long> conversationIds) {
        throw new UnsupportedOperationException("MAM-MUC cannot perform this operation");
    }

    @Override
    public Conversation getConversation(String ownerJid, String withJid, Date start) {
        throw new UnsupportedOperationException("MAM-MUC cannot perform this operation");
    }

    @Override
    public Conversation getConversation(Long conversationId) {
        throw new UnsupportedOperationException("MAM-MUC cannot perform this operation");
    }
}
