import { db } from '../config/database.js';
import { emails, contacts } from '../db/schema.js';
import { eq, and, or, desc, like, sql, inArray } from 'drizzle-orm';
import { NotFoundError } from '../utils/errors.js';

/**
 * Get all emails for an email account (only from added contacts)
 */
export const getEmailsForAccount = async (emailAccountId) => {
  const accountEmails = await db
    .select({
      id: emails.id,
      jmapId: emails.jmapId,
      messageId: emails.messageId,
      threadId: emails.threadId,
      inReplyTo: emails.inReplyTo,
      from: emails.from,
      to: emails.to,
      subject: emails.subject,
      body: emails.body,
      isRead: emails.isRead,
      sentByUserId: emails.sentByUserId,
      date: emails.date,
      createdAt: emails.createdAt,
      companyId: emails.companyId,
      emailAccountId: emails.emailAccountId,
      contact: {
        id: contacts.id,
        email: contacts.email,
        name: contacts.name,
      },
    })
    .from(emails)
    .innerJoin(contacts, eq(emails.contactId, contacts.id))
    .where(eq(emails.emailAccountId, emailAccountId))
    .orderBy(desc(emails.date));

  return accountEmails;
};

/**
 * Get emails by thread ID
 */
export const getEmailThread = async (emailAccountId, threadId) => {
  const thread = await db
    .select()
    .from(emails)
    .where(
      and(
        eq(emails.emailAccountId, emailAccountId),
        eq(emails.threadId, threadId)
      )
    )
    .orderBy(emails.date);

  if (thread.length === 0) {
    throw new NotFoundError('Thread nenájdený');
  }

  return thread;
};

/**
 * Search emails (from, to, subject)
 */
export const searchEmails = async (emailAccountId, query) => {
  if (!query || query.trim().length < 2) {
    throw new Error('Vyhľadávací výraz musí mať aspoň 2 znaky');
  }

  const searchPattern = `%${query}%`;
  const conditions = [
    eq(emails.emailAccountId, emailAccountId),
    or(
      like(emails.from, searchPattern),
      like(emails.to, searchPattern),
      like(emails.subject, searchPattern)
    ),
  ];

  const results = await db
    .select()
    .from(emails)
    .where(and(...conditions))
    .orderBy(desc(emails.date))
    .limit(50);

  return results;
};

/**
 * Get unread email count for an email account (only from added contacts)
 */
export const getUnreadCountForAccount = async (emailAccountId) => {
  const result = await db
    .select({ count: sql`count(*)::int` })
    .from(emails)
    .innerJoin(contacts, eq(emails.contactId, contacts.id))
    .where(
      and(
        eq(emails.emailAccountId, emailAccountId),
        eq(emails.isRead, false)
      )
    );

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

/**
 * Get unread email count summary for all user's email accounts (only from added contacts)
 */
export const getUnreadCountSummary = async (emailAccountIds) => {
  if (!emailAccountIds || emailAccountIds.length === 0) {
    return {
      totalUnread: 0,
      byAccount: [],
    };
  }

  // Get unread count per email account (only from added contacts)
  const accountCounts = await db
    .select({
      emailAccountId: emails.emailAccountId,
      count: sql`count(*)::int`,
    })
    .from(emails)
    .innerJoin(contacts, eq(emails.contactId, contacts.id))
    .where(
      and(
        inArray(emails.emailAccountId, emailAccountIds),
        eq(emails.isRead, false)
      )
    )
    .groupBy(emails.emailAccountId);

  const totalUnread = accountCounts.reduce((sum, acc) => sum + acc.count, 0);

  return {
    totalUnread,
    byAccount: accountCounts,
  };
};

/**
 * Get unread count by contact for an email account
 */
export const getUnreadCountByContact = async (emailAccountId) => {
  const contactCounts = await db
    .select({
      contactId: emails.contactId,
      contactEmail: contacts.email,
      contactName: contacts.name,
      count: sql`count(*)::int`,
    })
    .from(emails)
    .leftJoin(contacts, eq(emails.contactId, contacts.id))
    .where(
      and(
        eq(emails.emailAccountId, emailAccountId),
        eq(emails.isRead, false)
      )
    )
    .groupBy(emails.contactId, contacts.email, contacts.name);

  return contactCounts;
};

/**
 * Mark thread as read
 */
export const markThreadAsRead = async (emailAccountId, threadId) => {
  const updated = await db
    .update(emails)
    .set({ isRead: true, updatedAt: new Date() })
    .where(
      and(
        eq(emails.emailAccountId, emailAccountId),
        eq(emails.threadId, threadId),
        eq(emails.isRead, false)
      )
    )
    .returning();

  return updated.length;
};

/**
 * Mark all emails from a contact as read
 */
export const markContactEmailsAsRead = async (contactId, emailAccountId) => {
  const updated = await db
    .update(emails)
    .set({ isRead: true, updatedAt: new Date() })
    .where(
      and(
        eq(emails.emailAccountId, emailAccountId),
        eq(emails.contactId, contactId)
      )
    )
    .returning();

  return updated.length;
};

/**
 * Get contact by email address for specific email account
 */
export const getContactByEmail = async (emailAccountId, contactEmail) => {
  const [contact] = await db
    .select()
    .from(contacts)
    .where(
      and(
        eq(contacts.emailAccountId, emailAccountId),
        eq(contacts.email, contactEmail)
      )
    )
    .limit(1);

  return contact || null;
};

/**
 * Get contact emails with unread count
 */
export const getContactEmailsWithUnread = async (emailAccountId, contactId) => {
  const contactEmails = await db
    .select()
    .from(emails)
    .where(
      and(
        eq(emails.emailAccountId, emailAccountId),
        eq(emails.contactId, contactId)
      )
    )
    .orderBy(desc(emails.date));

  return contactEmails;
};
