using GLib;
using Gee;

public class ConversationListModel : Object, ListModel 
{
    public SortedSet<Conversation> conversations {
        owned get { return _conversations.read_only_view; }
    }

    private SortedSet<Conversation> _conversations;
    
    public ConversationListModel() {
        _conversations = new TreeSet<Conversation>((a, b) => {
            // Sort by date in descending order (newest first)
            return (int)(b.date - a.date);
        });

        weak ConversationListModel self = this;
        Repository.get_instance().conversations_updated.connect(() => {
            self.load_conversations();
        });

        Repository.get_instance().messages_updated.connect((conversation_guid) => {
            self.load_conversations();
        });

        Repository.get_instance().reconnected.connect(() => {
            // Trigger a sync-list to get the latest conversations
            try {
                Repository.get_instance().sync_conversation_list();
            } catch (GLib.Error e) {
                warning("Failed to sync conversation list: %s", e.message);
            }
        });
    }

    public Conversation? get_conversation(string guid) {
        foreach (var conv in _conversations) {
            if (conv.guid == guid) {
                return conv;
            }
        }

        return null;
    }
    
    public void load_conversations() {
        try {
            Conversation[] new_conversations = Repository.get_instance().get_conversations();
            
            // Create a map of old conversations for quick lookup
            var old_conversations_map = new HashMap<string, Conversation>();
            foreach (var conv in _conversations) {
                old_conversations_map[conv.guid] = conv;
            }
            
            // Create a map of new conversations for quick lookup
            var new_conversations_map = new HashMap<string, Conversation>();
            foreach (var conv in new_conversations) {
                new_conversations_map[conv.guid] = conv;
            }
            
            // Find removed conversations
            var removed_positions = new ArrayList<uint>();
            var current_position = 0;
            foreach (var old_conv in _conversations) {
                if (!new_conversations_map.has_key(old_conv.guid)) {
                    removed_positions.add(current_position);
                }
                current_position++;
            }
            
            // Remove conversations in reverse order to maintain correct positions
            for (int i = removed_positions.size - 1; i >= 0; i--) {
                var pos = removed_positions[i];
                _conversations.remove(_conversations.to_array()[pos]);
                items_changed(pos, 1, 0);
            }
            
            // Find added conversations and changed conversations
            var added_conversations = new ArrayList<Conversation>();
            var changed_conversations = new ArrayList<Conversation>();
            foreach (var new_conv in new_conversations) {
                if (!old_conversations_map.has_key(new_conv.guid)) {
                    added_conversations.add(new_conv);
                } else {
                    var old_conv = old_conversations_map[new_conv.guid];
                    if (!old_conv.equals(new_conv)) {
                        changed_conversations.add(new_conv);
                    }
                }
            }
            
            // Add new conversations
            foreach (var conv in added_conversations) {
                _conversations.add(conv);
                // Find the position by counting how many items are before this one
                uint pos = 0;
                foreach (var existing_conv in _conversations) {
                    if (existing_conv.guid == conv.guid) break;
                    pos++;
                }
                items_changed(pos, 0, 1);
            }
            
            // Update changed conversations
            foreach (var conv in changed_conversations) {
                // Find position of old conversation
                uint old_pos = 0;
                var old_conv = old_conversations_map[conv.guid];
                foreach (var existing_conv in _conversations) {
                    if (existing_conv.guid == old_conv.guid) break;
                    old_pos++;
                }

                // Remove the old one
                _conversations.remove(old_conv);

                // Add the new one
                _conversations.add(conv);

                // Find the new (sorted) position
                uint new_pos = 0;
                foreach (var existing_conv in _conversations) {
                    if (existing_conv.guid == conv.guid) break;
                    new_pos++;
                }

                // Notify of the change
                items_changed(old_pos, 1, 0);
                items_changed(new_pos, 0, 1);
            }
        } catch (Error e) {
            warning("Failed to load conversations: %s", e.message);
        }
    }
    
    // ListModel implementation
    public Type get_item_type() {
        return typeof(Conversation);
    }
    
    public uint get_n_items() {
        return _conversations.size;
    }
    
    public Object? get_item(uint position) {
        return _conversations.to_array()[position];
    }
}