import { eq, and, gte, lt } from 'drizzle-orm';
import { db } from '../../config/database.js';
import { events, eventUsers, users, emailAccounts, userEmailAccounts } from '../../db/schema.js';
import { decryptPassword } from '../../utils/password.js';
import { jmapRequest, getMailboxes, getIdentities } from '../../services/jmap/client.js';
import { logger } from '../../utils/logger.js';
import {
  generateEventNotificationHtml,
  generateEventNotificationText,
  generateEventNotificationSubject,
} from './email-template.js';

/**
 * Get sender email account credentials from database
 * @returns {Promise<Object|null>} Sender account with decrypted password
 */
const getSenderAccount = async () => {
  const senderEmail = process.env.NOTIFICATION_SENDER_EMAIL;

  if (!senderEmail) {
    logger.error('NOTIFICATION_SENDER_EMAIL nie je nastavený v .env');
    return null;
  }

  const [account] = await db
    .select()
    .from(emailAccounts)
    .where(eq(emailAccounts.email, senderEmail))
    .limit(1);

  if (!account) {
    logger.error(`Email účet ${senderEmail} nebol nájdený v databáze`);
    return null;
  }

  try {
    const decryptedPassword = decryptPassword(account.emailPassword);
    return {
      id: account.id,
      email: account.email,
      password: decryptedPassword,
      jmapAccountId: account.jmapAccountId,
    };
  } catch (error) {
    logger.error(`Nepodarilo sa dešifrovať heslo pre ${senderEmail}`, error);
    return null;
  }
};

/**
 * Get tomorrow's date range (start of day to end of day)
 * @returns {{ startOfTomorrow: Date, endOfTomorrow: Date }}
 */
const getTomorrowRange = () => {
  const now = new Date();

  // Start of tomorrow (00:00:00)
  const startOfTomorrow = new Date(now);
  startOfTomorrow.setDate(startOfTomorrow.getDate() + 1);
  startOfTomorrow.setHours(0, 0, 0, 0);

  // End of tomorrow (23:59:59.999)
  const endOfTomorrow = new Date(startOfTomorrow);
  endOfTomorrow.setHours(23, 59, 59, 999);

  return { startOfTomorrow, endOfTomorrow };
};

/**
 * Get events starting tomorrow with assigned users
 * @returns {Promise<Array>} Events with user info
 */
const getTomorrowEvents = async () => {
  const { startOfTomorrow, endOfTomorrow } = getTomorrowRange();

  logger.info(`Hľadám udalosti od ${startOfTomorrow.toISOString()} do ${endOfTomorrow.toISOString()}`);

  // Get events starting tomorrow
  const tomorrowEvents = await db
    .select({
      eventId: events.id,
      title: events.title,
      description: events.description,
      type: events.type,
      start: events.start,
      end: events.end,
      userId: users.id,
      username: users.username,
      firstName: users.firstName,
      lastName: users.lastName,
    })
    .from(events)
    .innerJoin(eventUsers, eq(events.id, eventUsers.eventId))
    .innerJoin(users, eq(eventUsers.userId, users.id))
    .where(
      and(
        gte(events.start, startOfTomorrow),
        lt(events.start, endOfTomorrow)
      )
    );

  return tomorrowEvents;
};

/**
 * Get user's primary email address
 * @param {string} userId
 * @returns {Promise<string|null>}
 */
const getUserEmail = async (userId) => {
  const [result] = await db
    .select({
      email: emailAccounts.email,
    })
    .from(userEmailAccounts)
    .innerJoin(emailAccounts, eq(userEmailAccounts.emailAccountId, emailAccounts.id))
    .where(
      and(
        eq(userEmailAccounts.userId, userId),
        eq(userEmailAccounts.isPrimary, true)
      )
    )
    .limit(1);

  return result?.email || null;
};

/**
 * Send email via JMAP (simplified version for notifications)
 * @param {Object} jmapConfig - JMAP configuration
 * @param {string} to - Recipient email
 * @param {string} subject - Email subject
 * @param {string} htmlBody - HTML body
 * @param {string} textBody - Plain text body
 * @returns {Promise<boolean>} Success status
 */
const sendNotificationEmail = async (jmapConfig, to, subject, htmlBody, textBody) => {
  try {
    // Get mailboxes
    const mailboxes = await getMailboxes(jmapConfig);
    const sentMailbox = mailboxes.find((m) => m.role === 'sent' || m.name === 'Sent');

    if (!sentMailbox) {
      logger.error('Priečinok Odoslané nebol nájdený');
      return false;
    }

    // Create email with HTML body
    const createResponse = await jmapRequest(jmapConfig, [
      [
        'Email/set',
        {
          accountId: jmapConfig.accountId,
          create: {
            draft: {
              mailboxIds: {
                [sentMailbox.id]: true,
              },
              keywords: {
                $draft: true,
              },
              from: [{ email: jmapConfig.username }],
              to: [{ email: to }],
              subject: subject,
              htmlBody: [{ partId: 'html', type: 'text/html' }],
              textBody: [{ partId: 'text', type: 'text/plain' }],
              bodyValues: {
                html: { value: htmlBody },
                text: { value: textBody },
              },
            },
          },
        },
        'set1',
      ],
    ]);

    const createdEmailId = createResponse.methodResponses[0][1].created?.draft?.id;

    if (!createdEmailId) {
      logger.error('Nepodarilo sa vytvoriť koncept emailu', createResponse.methodResponses[0][1].notCreated);
      return false;
    }

    // Get identity for sending
    const identities = await getIdentities(jmapConfig);
    const identity = identities.find((i) => i.email === jmapConfig.username) || identities[0];

    if (!identity) {
      logger.error('Nenašla sa identita pre odosielanie emailov');
      return false;
    }

    // Submit the email
    const submitResponse = await jmapRequest(jmapConfig, [
      [
        'EmailSubmission/set',
        {
          accountId: jmapConfig.accountId,
          create: {
            submission: {
              emailId: createdEmailId,
              identityId: identity.id,
            },
          },
        },
        'submit1',
      ],
    ]);

    const submissionId = submitResponse.methodResponses[0][1].created?.submission?.id;

    if (!submissionId) {
      logger.error('Nepodarilo sa odoslať email', submitResponse.methodResponses[0][1].notCreated);
      return false;
    }

    return true;
  } catch (error) {
    logger.error(`Chyba pri odosielaní emailu na ${to}`, error);
    return false;
  }
};

/**
 * Get sender account by user ID (admin's primary email)
 * @param {string} userId - Admin user ID
 * @returns {Promise<Object|null>} Sender account with decrypted password
 */
const getSenderAccountByUserId = async (userId) => {
  const [result] = await db
    .select({
      id: emailAccounts.id,
      email: emailAccounts.email,
      emailPassword: emailAccounts.emailPassword,
      jmapAccountId: emailAccounts.jmapAccountId,
    })
    .from(userEmailAccounts)
    .innerJoin(emailAccounts, eq(userEmailAccounts.emailAccountId, emailAccounts.id))
    .where(
      and(
        eq(userEmailAccounts.userId, userId),
        eq(userEmailAccounts.isPrimary, true)
      )
    )
    .limit(1);

  if (!result) {
    logger.error(`Používateľ ${userId} nemá nastavený primárny email účet`);
    return null;
  }

  try {
    const decryptedPassword = decryptPassword(result.emailPassword);
    return {
      id: result.id,
      email: result.email,
      password: decryptedPassword,
      jmapAccountId: result.jmapAccountId,
    };
  } catch (error) {
    logger.error(`Nepodarilo sa dešifrovať heslo pre ${result.email}`, error);
    return null;
  }
};

/**
 * Send notification for a single event to all assigned users
 * @param {string} eventId - Event ID
 * @param {string} adminUserId - Admin user ID (sender)
 * @returns {Promise<{ sent: number, failed: number, skipped: number, eventTitle: string }>}
 */
export const sendSingleEventNotification = async (eventId, adminUserId) => {
  logger.info(`=== Odosielam notifikácie pre event ${eventId} od admina ${adminUserId} ===`);

  const stats = { sent: 0, failed: 0, skipped: 0, eventTitle: '' };

  // Get admin's primary email account
  const senderAccount = await getSenderAccountByUserId(adminUserId);
  if (!senderAccount) {
    throw new Error('Nemáte nastavený primárny email účet. Nastavte ho v profile.');
  }

  const jmapConfig = {
    server: process.env.JMAP_SERVER,
    username: senderAccount.email,
    password: senderAccount.password,
    accountId: senderAccount.jmapAccountId,
  };

  logger.info(`Odosielam z účtu: ${senderAccount.email}`);

  // Get the event with assigned users
  const eventData = await db
    .select({
      eventId: events.id,
      title: events.title,
      description: events.description,
      type: events.type,
      start: events.start,
      end: events.end,
      userId: users.id,
      username: users.username,
      firstName: users.firstName,
      lastName: users.lastName,
    })
    .from(events)
    .innerJoin(eventUsers, eq(events.id, eventUsers.eventId))
    .innerJoin(users, eq(eventUsers.userId, users.id))
    .where(eq(events.id, eventId));

  if (eventData.length === 0) {
    throw new Error('Event nebol nájdený alebo nemá priradených používateľov');
  }

  stats.eventTitle = eventData[0].title;
  logger.info(`Event: ${stats.eventTitle}, priradených používateľov: ${eventData.length}`);

  // Send notifications to each assigned user
  for (const row of eventData) {
    const { userId, username, firstName } = row;
    const event = {
      id: row.eventId,
      title: row.title,
      description: row.description,
      type: row.type,
      start: row.start,
      end: row.end,
    };

    // Get user's email
    const userEmail = await getUserEmail(userId);

    if (!userEmail) {
      stats.skipped++;
      continue;
    }

    // Generate email content
    const subject = generateEventNotificationSubject(event);
    const htmlBody = generateEventNotificationHtml({ firstName, username, event });
    const textBody = generateEventNotificationText({ firstName, username, event });

    // Send email
    logger.info(`Odosielam notifikáciu pre ${username}`);

    const success = await sendNotificationEmail(jmapConfig, userEmail, subject, htmlBody, textBody);

    if (success) {
      logger.success(`Email úspešne odoslaný na ${userEmail}`);
      stats.sent++;
    } else {
      logger.error(`Nepodarilo sa odoslať email na ${userEmail}`);
      stats.failed++;
    }
  }

  logger.info(`=== Hotovo: odoslaných ${stats.sent}, neúspešných ${stats.failed}, preskočených ${stats.skipped} ===`);

  return stats;
};

/**
 * Main function to send event notifications
 * @returns {Promise<{ sent: number, failed: number, skipped: number }>}
 */
export const sendEventNotifications = async () => {
  logger.info('=== Spúšťam kontrolu zajtrajších udalostí ===');

  const stats = { sent: 0, failed: 0, skipped: 0 };

  // Get sender account
  const senderAccount = await getSenderAccount();
  if (!senderAccount) {
    logger.error('Nemôžem pokračovať bez platného odosielacieho účtu');
    return stats;
  }

  const jmapConfig = {
    server: process.env.JMAP_SERVER,
    username: senderAccount.email,
    password: senderAccount.password,
    accountId: senderAccount.jmapAccountId,
  };

  // Get tomorrow's events with assigned users
  const tomorrowEvents = await getTomorrowEvents();

  if (tomorrowEvents.length === 0) {
    logger.info('Žiadne udalosti na zajtra');
    return stats;
  }

  logger.info(`Nájdených ${tomorrowEvents.length} priradení udalostí na zajtra`);

  // Group events by user to avoid duplicate notifications for same event
  const userNotifications = new Map();

  for (const row of tomorrowEvents) {
    const key = `${row.userId}-${row.eventId}`;
    if (!userNotifications.has(key)) {
      userNotifications.set(key, {
        userId: row.userId,
        username: row.username,
        firstName: row.firstName,
        lastName: row.lastName,
        event: {
          id: row.eventId,
          title: row.title,
          description: row.description,
          type: row.type,
          start: row.start,
          end: row.end,
        },
      });
    }
  }

  logger.info(`Unikátnych notifikácií na odoslanie: ${userNotifications.size}`);

  // Send notifications
  for (const [key, data] of userNotifications) {
    const { userId, username, firstName, event } = data;

    // Get user's email
    const userEmail = await getUserEmail(userId);

    if (!userEmail) {
      stats.skipped++;
      continue;
    }

    // Generate email content
    const subject = generateEventNotificationSubject(event);
    const htmlBody = generateEventNotificationHtml({ firstName, username, event });
    const textBody = generateEventNotificationText({ firstName, username, event });

    // Send email
    logger.info(`Odosielam notifikáciu pre ${username} - udalosť: ${event.title}`);

    const success = await sendNotificationEmail(jmapConfig, userEmail, subject, htmlBody, textBody);

    if (success) {
      logger.success(`Email úspešne odoslaný na ${userEmail}`);
      stats.sent++;
    } else {
      logger.error(`Nepodarilo sa odoslať email na ${userEmail}`);
      stats.failed++;
    }
  }

  logger.info(`=== Hotovo: odoslaných ${stats.sent}, neúspešných ${stats.failed}, preskočených ${stats.skipped} ===`);

  return stats;
};
