import { db } from '../config/database.js';
import { messages, users } from '../db/schema.js';
import { eq, and, or, desc, ne, sql } from 'drizzle-orm';
import { NotFoundError } from '../utils/errors.js';
import { sendPushNotification } from './push.service.js';
import { logger } from '../utils/logger.js';
import { getGroupUnreadCount } from './group.service.js';

/**
 * Get all conversations for a user
 * Returns list of users with last message preview
 */
export const getConversations = async (userId) => {
  // Get all messages where user is sender or receiver (not deleted)
  const userMessages = await db
    .select({
      id: messages.id,
      senderId: messages.senderId,
      receiverId: messages.receiverId,
      content: messages.content,
      isRead: messages.isRead,
      createdAt: messages.createdAt,
      deletedBySender: messages.deletedBySender,
      deletedByReceiver: messages.deletedByReceiver,
    })
    .from(messages)
    .where(
      or(
        and(eq(messages.senderId, userId), eq(messages.deletedBySender, false)),
        and(eq(messages.receiverId, userId), eq(messages.deletedByReceiver, false))
      )
    )
    .orderBy(desc(messages.createdAt));

  // Group by conversation partner and get last message
  const conversationsMap = new Map();

  for (const msg of userMessages) {
    const partnerId = msg.senderId === userId ? msg.receiverId : msg.senderId;

    if (!conversationsMap.has(partnerId)) {
      conversationsMap.set(partnerId, {
        partnerId,
        lastMessage: msg,
        unreadCount: 0,
      });
    }

    // Count unread messages (only where user is receiver)
    if (msg.receiverId === userId && !msg.isRead) {
      const conv = conversationsMap.get(partnerId);
      conv.unreadCount++;
    }
  }

  // Get user details for each partner
  const partnerIds = Array.from(conversationsMap.keys());

  if (partnerIds.length === 0) {
    return [];
  }

  const partners = await db
    .select({
      id: users.id,
      username: users.username,
      firstName: users.firstName,
      lastName: users.lastName,
      lastSeen: users.lastSeen,
    })
    .from(users)
    .where(sql`${users.id} IN ${partnerIds}`);

  const partnersMap = new Map(partners.map(p => [p.id, p]));

  // Build final conversation list
  const conversations = Array.from(conversationsMap.values())
    .map(conv => ({
      user: partnersMap.get(conv.partnerId),
      lastMessage: {
        content: conv.lastMessage.content,
        createdAt: conv.lastMessage.createdAt,
        isMine: conv.lastMessage.senderId === userId,
      },
      unreadCount: conv.unreadCount,
    }))
    .filter(conv => conv.user) // Filter out any invalid users
    .sort((a, b) => new Date(b.lastMessage.createdAt) - new Date(a.lastMessage.createdAt));

  return conversations;
};

/**
 * Get messages with a specific user
 */
export const getConversation = async (userId, partnerId) => {
  // Verify partner exists
  const [partner] = await db
    .select({
      id: users.id,
      username: users.username,
      firstName: users.firstName,
      lastName: users.lastName,
    })
    .from(users)
    .where(eq(users.id, partnerId))
    .limit(1);

  if (!partner) {
    throw new NotFoundError('Používateľ nenájdený');
  }

  // Get messages between these two users
  const conversationMessages = await db
    .select({
      id: messages.id,
      senderId: messages.senderId,
      receiverId: messages.receiverId,
      content: messages.content,
      isRead: messages.isRead,
      createdAt: messages.createdAt,
    })
    .from(messages)
    .where(
      and(
        or(
          and(
            eq(messages.senderId, userId),
            eq(messages.receiverId, partnerId),
            eq(messages.deletedBySender, false)
          ),
          and(
            eq(messages.senderId, partnerId),
            eq(messages.receiverId, userId),
            eq(messages.deletedByReceiver, false)
          )
        )
      )
    )
    .orderBy(messages.createdAt);

  // Mark messages as read (where user is receiver)
  await db
    .update(messages)
    .set({ isRead: true, updatedAt: new Date() })
    .where(
      and(
        eq(messages.senderId, partnerId),
        eq(messages.receiverId, userId),
        eq(messages.isRead, false)
      )
    );

  return {
    partner,
    messages: conversationMessages.map(msg => ({
      id: msg.id,
      content: msg.content,
      isMine: msg.senderId === userId,
      isRead: msg.isRead,
      createdAt: msg.createdAt,
    })),
  };
};

/**
 * Send a message to another user
 */
export const sendMessage = async (senderId, receiverId, content) => {
  // Verify receiver exists
  const [receiver] = await db
    .select({ id: users.id })
    .from(users)
    .where(eq(users.id, receiverId))
    .limit(1);

  if (!receiver) {
    throw new NotFoundError('Príjemca nenájdený');
  }

  // Cannot send message to yourself
  if (senderId === receiverId) {
    throw new Error('Nemôžete poslať správu sám sebe');
  }

  const [newMessage] = await db
    .insert(messages)
    .values({
      senderId,
      receiverId,
      content: content.trim(),
    })
    .returning();

  // Send push notification to receiver
  try {
    const [sender] = await db
      .select({ firstName: users.firstName, username: users.username })
      .from(users)
      .where(eq(users.id, senderId))
      .limit(1);

    const senderName = sender?.firstName || sender?.username || 'Niekto';

    await sendPushNotification(receiverId, {
      title: `Nová správa od ${senderName}`,
      body: content.trim().substring(0, 100) + (content.length > 100 ? '...' : ''),
      icon: '/icon-192.png',
      badge: '/badge-72.png',
      data: { url: `/chat/${senderId}` },
    });
  } catch (error) {
    logger.error('Failed to send push notification for direct message', error);
  }

  return {
    id: newMessage.id,
    content: newMessage.content,
    isMine: true,
    isRead: false,
    createdAt: newMessage.createdAt,
  };
};

/**
 * Delete conversation for a user (soft delete)
 */
export const deleteConversation = async (userId, partnerId) => {
  // Mark all messages as deleted for this user
  // Messages where user is sender
  await db
    .update(messages)
    .set({ deletedBySender: true, updatedAt: new Date() })
    .where(
      and(
        eq(messages.senderId, userId),
        eq(messages.receiverId, partnerId)
      )
    );

  // Messages where user is receiver
  await db
    .update(messages)
    .set({ deletedByReceiver: true, updatedAt: new Date() })
    .where(
      and(
        eq(messages.senderId, partnerId),
        eq(messages.receiverId, userId)
      )
    );

  return { success: true };
};

/**
 * Get unread message count for a user (direct messages only)
 */
export const getDirectUnreadCount = async (userId) => {
  const result = await db
    .select({ count: sql`count(*)::int` })
    .from(messages)
    .where(
      and(
        eq(messages.receiverId, userId),
        eq(messages.isRead, false),
        eq(messages.deletedByReceiver, false)
      )
    );

  return result[0]?.count || 0;
};

/**
 * Get combined unread message count for a user (direct + group)
 */
export const getUnreadCount = async (userId) => {
  const [directCount, groupCount] = await Promise.all([
    getDirectUnreadCount(userId),
    getGroupUnreadCount(userId),
  ]);

  return {
    directCount,
    groupCount,
    total: directCount + groupCount,
  };
};

/**
 * Get all CRM users available for chat (excluding current user)
 */
export const getChatUsers = async (currentUserId) => {
  const chatUsers = await db
    .select({
      id: users.id,
      username: users.username,
      firstName: users.firstName,
      lastName: users.lastName,
      role: users.role,
      lastLogin: users.lastLogin,
      lastSeen: users.lastSeen,
    })
    .from(users)
    .where(ne(users.id, currentUserId))
    .orderBy(users.username);

  return chatUsers;
};
