import { countDistinct, eq, not } from "drizzle-orm";
import { database } from "@db/database";
import { chat, reply } from "@db/schema";
import { PAGINATION } from "@db/constants";

export const getUnreadMessages = async (page = 1) => {
  try {
    return await database.transaction(async (tx) => {
      const chats = await tx.query.chat
        .findMany({
          where: not(chat.isRead),
          offset: (page - 1) * PAGINATION,
          limit: page * PAGINATION,
          orderBy: chat.time,
        })
        .prepare()
        .execute();

      const amount = await tx
        .select({ total: countDistinct(chat.id) })
        .from(chat)
        .where(not(chat.isRead))
        .prepare()
        .execute();

      const total = amount.map((e) => e.total).reduce((a, b) => a + b);

      return {
        total,
        chats,
        page,
        total_pages: Math.ceil(total / PAGINATION),
      };
    });
  } catch (error) {
    console.error(error);
    throw new Error(JSON.stringify(error, null, 2));
  }
};

export const getAllMessages = async (page = 1) => {
  try {
    return await database.transaction(async (tx) => {
      const chats = await tx.query.chat
        .findMany({
          offset: (page - 1) * PAGINATION,
          limit: page * PAGINATION,
          orderBy: chat.time,
        })
        .prepare()
        .execute();
      const amount = await tx
        .select({ total: countDistinct(chat.id) })
        .from(chat)
        .prepare()
        .execute();

      const total = amount.map((e) => e.total).reduce((a, b) => a + b);

      return {
        total,
        chats,
        page,
        total_pages: Math.ceil(total / PAGINATION),
      };
    });
  } catch (error) {
    console.error(error);
    throw new Error(JSON.stringify(error, null, 2));
  }
};

export const getOneMessage = async (id: number, page = 1) => {
  try {
    return await database.transaction(async (tx) => {
      const message = await tx.query.chat
        .findFirst({
          where: eq(chat.id, id),
          orderBy: chat.time,
          with: {
            replies: {
              orderBy: reply.time,
              with: {
                staff: {
                  columns: {
                    id: true,
                    username: true,
                    firstName: true,
                    lastName: true,
                    email: true,
                    position: true,
                  },
                },
              },
              limit: PAGINATION,
            },
          },
        })
        .prepare()
        .execute();

      const amount = message
        ? await tx
            .select({ total: countDistinct(reply.id) })
            .from(reply)
            .where(eq(reply.chatId, message.id))
            .prepare()
            .execute()
        : [];

      const total_replies = amount.map((e) => e.total).reduce((a, b) => a + b);

      await tx
        .update(chat)
        .set({
          isRead: true,
        })
        .where(eq(chat.id, id))
        .prepare()
        .execute();

      return {
        chat: message,
        total_replies,
        page,
      };
    });
  } catch (error) {
    console.error(error);
    throw new Error(JSON.stringify(error, null, 2));
  }
};

export const getOneReply = async (id: number) => {
  try {
    return await database.query.reply
      .findFirst({
        where: eq(reply.id, id),
        with: {
          message: true,
          staff: true,
        },
      })
      .prepare()
      .execute();
  } catch (error) {
    console.error(error);
    throw new Error(JSON.stringify(error, null, 2));
  }
};

export const markAsRead = async (id: number) => {
  try {
    return await database
      .update(chat)
      .set({
        isRead: true,
      })
      .where(eq(chat.id, id))
      .prepare()
      .execute();
  } catch (error) {
    console.error(error);
    throw new Error(JSON.stringify(error, null, 2));
  }
};

export const replyToMessage = async ({
  chatId,
  repliedBy,
  message,
}: {
  chatId: number;
  repliedBy: number;
  message: string;
}) => {
  try {
    const replyChat = await database
      .insert(reply)
      .values({
        repliedBy,
        chatId,
        reply: message,
      })
      .prepare()
      .execute();

    await database
      .update(chat)
      .set({ isReplied: true })
      .where(eq(chat.id, chatId))
      .prepare()
      .execute();

    return replyChat;
  } catch (error) {
    console.error(error);
    throw new Error(JSON.stringify(error, null, 2));
  }
};

export const newMessage = async ({
  name,
  email,
  subject,
  message,
}: {
  name: string;
  email: string;
  subject: string;
  message: string;
}) => {
  try {
    return await database
      .insert(chat)
      .values({
        name,
        email,
        subject,
        message,
      })
      .prepare()
      .execute();
  } catch (error) {
    console.error(error);
    throw new Error(JSON.stringify(error, null, 2));
  }
};
