import { db } from '../config/database.js';
import { chatGroups, chatGroupMembers, groupMessages, users } from '../db/schema.js';
import { eq, and, desc, inArray, sql, gt } from 'drizzle-orm';
import { NotFoundError, ForbiddenError } from '../utils/errors.js';
import { sendPushNotificationToUsers } from './push.service.js';
import { logger } from '../utils/logger.js';

/**
 * Create a new group chat
 */
export const createGroup = async (name, creatorId, memberIds) => {
  const [group] = await db
    .insert(chatGroups)
    .values({
      name: name.trim(),
      createdById: creatorId,
    })
    .returning();

  // Add creator and all members (ensure unique)
  const allMemberIds = [...new Set([creatorId, ...memberIds])];

  await db.insert(chatGroupMembers).values(
    allMemberIds.map((userId) => ({
      groupId: group.id,
      userId,
    }))
  );

  return group;
};

/**
 * Get all groups for a user
 */
export const getUserGroups = async (userId) => {
  // Get groups where user is a member with lastReadAt
  const memberOf = await db
    .select({
      groupId: chatGroupMembers.groupId,
      lastReadAt: chatGroupMembers.lastReadAt,
    })
    .from(chatGroupMembers)
    .where(eq(chatGroupMembers.userId, userId));

  if (memberOf.length === 0) return [];

  const groupIds = memberOf.map((m) => m.groupId);
  const lastReadMap = memberOf.reduce((acc, m) => {
    acc[m.groupId] = m.lastReadAt;
    return acc;
  }, {});

  const groups = await db
    .select({
      id: chatGroups.id,
      name: chatGroups.name,
      createdById: chatGroups.createdById,
      createdAt: chatGroups.createdAt,
      updatedAt: chatGroups.updatedAt,
    })
    .from(chatGroups)
    .where(inArray(chatGroups.id, groupIds))
    .orderBy(desc(chatGroups.updatedAt));

  // Get last message, member count, and unread count for each group
  const result = await Promise.all(
    groups.map(async (group) => {
      const lastReadAt = lastReadMap[group.id];

      const [lastMessage, members, unreadResult] = await Promise.all([
        db
          .select({
            content: groupMessages.content,
            createdAt: groupMessages.createdAt,
            senderId: groupMessages.senderId,
            senderFirstName: users.firstName,
            senderUsername: users.username,
          })
          .from(groupMessages)
          .leftJoin(users, eq(groupMessages.senderId, users.id))
          .where(eq(groupMessages.groupId, group.id))
          .orderBy(desc(groupMessages.createdAt))
          .limit(1),
        db
          .select({ id: chatGroupMembers.id })
          .from(chatGroupMembers)
          .where(eq(chatGroupMembers.groupId, group.id)),
        // Count unread messages (messages after lastReadAt, not sent by current user)
        db
          .select({ count: sql`count(*)::int` })
          .from(groupMessages)
          .where(
            and(
              eq(groupMessages.groupId, group.id),
              gt(groupMessages.createdAt, lastReadAt),
              sql`${groupMessages.senderId} != ${userId}`
            )
          ),
      ]);

      return {
        ...group,
        lastMessage: lastMessage[0] ? {
          content: lastMessage[0].content,
          createdAt: lastMessage[0].createdAt,
          senderName: lastMessage[0].senderFirstName || lastMessage[0].senderUsername,
          isMine: lastMessage[0].senderId === userId,
        } : null,
        memberCount: members.length,
        unreadCount: unreadResult[0]?.count || 0,
        type: 'group',
      };
    })
  );

  return result;
};

/**
 * Get group details with members
 */
export const getGroupDetails = async (groupId, userId) => {
  // Verify user is member
  const [isMember] = await db
    .select()
    .from(chatGroupMembers)
    .where(
      and(
        eq(chatGroupMembers.groupId, groupId),
        eq(chatGroupMembers.userId, userId)
      )
    )
    .limit(1);

  if (!isMember) {
    throw new ForbiddenError('Nie ste členom tejto skupiny');
  }

  const [group] = await db
    .select()
    .from(chatGroups)
    .where(eq(chatGroups.id, groupId))
    .limit(1);

  if (!group) {
    throw new NotFoundError('Skupina nenájdená');
  }

  const members = await db
    .select({
      id: users.id,
      username: users.username,
      firstName: users.firstName,
      lastName: users.lastName,
      role: users.role,
    })
    .from(chatGroupMembers)
    .innerJoin(users, eq(chatGroupMembers.userId, users.id))
    .where(eq(chatGroupMembers.groupId, groupId));

  return {
    ...group,
    members,
  };
};

/**
 * Get messages for a group
 */
export const getGroupMessages = async (groupId, userId) => {
  // Verify user is member
  const [isMember] = await db
    .select()
    .from(chatGroupMembers)
    .where(
      and(
        eq(chatGroupMembers.groupId, groupId),
        eq(chatGroupMembers.userId, userId)
      )
    )
    .limit(1);

  if (!isMember) {
    throw new ForbiddenError('Nie ste členom tejto skupiny');
  }

  // Update lastReadAt for this user in this group
  await db
    .update(chatGroupMembers)
    .set({ lastReadAt: new Date() })
    .where(
      and(
        eq(chatGroupMembers.groupId, groupId),
        eq(chatGroupMembers.userId, userId)
      )
    );

  const messages = await db
    .select({
      id: groupMessages.id,
      content: groupMessages.content,
      createdAt: groupMessages.createdAt,
      senderId: groupMessages.senderId,
      senderUsername: users.username,
      senderFirstName: users.firstName,
      senderLastName: users.lastName,
    })
    .from(groupMessages)
    .leftJoin(users, eq(groupMessages.senderId, users.id))
    .where(eq(groupMessages.groupId, groupId))
    .orderBy(groupMessages.createdAt);

  return messages.map((msg) => ({
    id: msg.id,
    content: msg.content,
    createdAt: msg.createdAt,
    senderId: msg.senderId,
    senderName: msg.senderFirstName || msg.senderUsername || 'Neznámy',
    isMine: msg.senderId === userId,
  }));
};

/**
 * Send message to group
 */
export const sendGroupMessage = async (groupId, senderId, content) => {
  // Verify user is member
  const [isMember] = await db
    .select()
    .from(chatGroupMembers)
    .where(
      and(
        eq(chatGroupMembers.groupId, groupId),
        eq(chatGroupMembers.userId, senderId)
      )
    )
    .limit(1);

  if (!isMember) {
    throw new ForbiddenError('Nie ste členom tejto skupiny');
  }

  const [message] = await db
    .insert(groupMessages)
    .values({
      groupId,
      senderId,
      content: content.trim(),
    })
    .returning();

  // Update group's updatedAt
  await db
    .update(chatGroups)
    .set({ updatedAt: new Date() })
    .where(eq(chatGroups.id, groupId));

  // Send push notifications to all group members (except sender)
  try {
    const [group] = await db
      .select({ name: chatGroups.name })
      .from(chatGroups)
      .where(eq(chatGroups.id, groupId))
      .limit(1);

    const [sender] = await db
      .select({ firstName: users.firstName, username: users.username })
      .from(users)
      .where(eq(users.id, senderId))
      .limit(1);

    const members = await db
      .select({ userId: chatGroupMembers.userId })
      .from(chatGroupMembers)
      .where(eq(chatGroupMembers.groupId, groupId));

    const memberIds = members.map(m => m.userId);
    const senderName = sender?.firstName || sender?.username || 'Niekto';
    const groupName = group?.name || 'Skupina';

    await sendPushNotificationToUsers(
      memberIds,
      {
        title: `${groupName}`,
        body: `${senderName}: ${content.trim().substring(0, 80)}${content.length > 80 ? '...' : ''}`,
        icon: '/icon-192.png',
        badge: '/badge-72.png',
        data: { url: `/chat/group/${groupId}` },
      },
      senderId // exclude sender
    );
  } catch (error) {
    logger.error('Failed to send push notifications for group message', error);
  }

  return {
    id: message.id,
    content: message.content,
    createdAt: message.createdAt,
    senderId: message.senderId,
    isMine: true,
  };
};

/**
 * Add member to group
 */
export const addGroupMember = async (groupId, userId, requesterId) => {
  // Verify requester is member
  const [isMember] = await db
    .select()
    .from(chatGroupMembers)
    .where(
      and(
        eq(chatGroupMembers.groupId, groupId),
        eq(chatGroupMembers.userId, requesterId)
      )
    )
    .limit(1);

  if (!isMember) {
    throw new ForbiddenError('Nie ste členom tejto skupiny');
  }

  // Check if already member
  const [alreadyMember] = await db
    .select()
    .from(chatGroupMembers)
    .where(
      and(
        eq(chatGroupMembers.groupId, groupId),
        eq(chatGroupMembers.userId, userId)
      )
    )
    .limit(1);

  if (alreadyMember) {
    throw new Error('Používateľ je už členom skupiny');
  }

  // Verify user exists
  const [userExists] = await db
    .select({ id: users.id })
    .from(users)
    .where(eq(users.id, userId))
    .limit(1);

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

  await db.insert(chatGroupMembers).values({ groupId, userId });

  return { success: true };
};

/**
 * Remove member from group (or leave)
 */
export const removeGroupMember = async (groupId, userId, requesterId) => {
  // User can remove themselves
  if (userId !== requesterId) {
    // Check if requester is the creator
    const [group] = await db
      .select({ createdById: chatGroups.createdById })
      .from(chatGroups)
      .where(eq(chatGroups.id, groupId))
      .limit(1);

    if (!group || group.createdById !== requesterId) {
      throw new ForbiddenError('Nemáte oprávnenie odstrániť tohto člena');
    }
  }

  await db
    .delete(chatGroupMembers)
    .where(
      and(
        eq(chatGroupMembers.groupId, groupId),
        eq(chatGroupMembers.userId, userId)
      )
    );

  return { success: true };
};

/**
 * Update group name
 */
export const updateGroupName = async (groupId, name, requesterId) => {
  // Verify requester is the creator
  const [group] = await db
    .select({ createdById: chatGroups.createdById })
    .from(chatGroups)
    .where(eq(chatGroups.id, groupId))
    .limit(1);

  if (!group) {
    throw new NotFoundError('Skupina nenájdená');
  }

  if (group.createdById !== requesterId) {
    throw new ForbiddenError('Nemáte oprávnenie upraviť názov skupiny');
  }

  const [updated] = await db
    .update(chatGroups)
    .set({ name: name.trim(), updatedAt: new Date() })
    .where(eq(chatGroups.id, groupId))
    .returning();

  return updated;
};

/**
 * Delete group (only creator can delete)
 */
export const deleteGroup = async (groupId, requesterId) => {
  const [group] = await db
    .select({ createdById: chatGroups.createdById })
    .from(chatGroups)
    .where(eq(chatGroups.id, groupId))
    .limit(1);

  if (!group) {
    throw new NotFoundError('Skupina nenájdená');
  }

  if (group.createdById !== requesterId) {
    throw new ForbiddenError('Nemáte oprávnenie odstrániť túto skupinu');
  }

  await db.delete(chatGroups).where(eq(chatGroups.id, groupId));

  return { success: true };
};

/**
 * Get total unread group messages count for a user
 */
export const getGroupUnreadCount = async (userId) => {
  // Get all groups user is a member of with lastReadAt
  const memberOf = await db
    .select({
      groupId: chatGroupMembers.groupId,
      lastReadAt: chatGroupMembers.lastReadAt,
    })
    .from(chatGroupMembers)
    .where(eq(chatGroupMembers.userId, userId));

  if (memberOf.length === 0) return 0;

  // Count unread messages across all groups
  let totalUnread = 0;

  for (const membership of memberOf) {
    const result = await db
      .select({ count: sql`count(*)::int` })
      .from(groupMessages)
      .where(
        and(
          eq(groupMessages.groupId, membership.groupId),
          gt(groupMessages.createdAt, membership.lastReadAt),
          sql`${groupMessages.senderId} != ${userId}`
        )
      );
    totalUnread += result[0]?.count || 0;
  }

  return totalUnread;
};
