import { UserEmailSettingsRepository } from '../models/repositories/UserEmailSettingsRepository';
import { UserRepository } from '../models/repositories/UserRepository';
import { EMAIL_PROVIDER_PRESETS, EmailProviderKey } from '../config/appConfig';
import { encrypt, decrypt } from '../utils/crypto';
import { AppError } from '../utils/AppError';
import { UserEmailSettings } from '../models/entities/UserEmailSettings.entity';
import nodemailer from 'nodemailer';
import { env } from '../config/env';
import {
  createPlatformMailSender,
  isPlatformMailConfigured,
  SendMailOptions,
  SmtpMailSender,
} from './mail/MailSender';

export class UserEmailSettingsService {
  constructor(
    private settingsRepo = new UserEmailSettingsRepository(),
    private userRepo = new UserRepository(),
  ) {}

  async getForUser(userId: string) {
    const user = await this.userRepo.findById(userId);
    if (!user) throw new AppError('not_found', 404, 'User not found.');

    let settings = await this.settingsRepo.findByUserId(userId);
    if (!settings) {
      settings = await this.settingsRepo.upsert(userId, {
        provider: 'platform',
        fromEmail: user.email,
        fromName: user.fullName,
      });
    }
    return settings;
  }

  async update(userId: string, input: {
    provider: EmailProviderKey;
    smtpHost?: string | null;
    smtpPort?: number;
    smtpUser?: string | null;
    smtpPassword?: string | null;
    fromEmail?: string | null;
    fromName?: string | null;
    notificationsEnabled?: boolean;
    sendFromUserEmail?: boolean;
    sendReminderToGuest?: boolean;
    sendReminderToHost?: boolean;
    sendFollowUpEnabled?: boolean;
    sendPostMeetingSurvey?: boolean;
  }) {
    const user = await this.userRepo.findById(userId);
    if (!user) throw new AppError('not_found', 404, 'User not found.');

    const preset = EMAIL_PROVIDER_PRESETS[input.provider];
    const existing = await this.settingsRepo.findByUserId(userId);

    const data: Partial<UserEmailSettings> = {
      provider: input.provider,
      smtpHost: input.provider === 'platform' ? null : (input.smtpHost ?? preset.smtpHost),
      smtpPort: input.smtpPort ?? preset.smtpPort,
      smtpUser: input.provider === 'platform' ? null : input.smtpUser ?? null,
      fromEmail: input.fromEmail ?? user.email,
      fromName: input.fromName ?? user.fullName,
      notificationsEnabled: input.notificationsEnabled ?? true,
      sendFromUserEmail: input.sendFromUserEmail ?? false,
      sendReminderToGuest: input.sendReminderToGuest ?? true,
      sendReminderToHost: input.sendReminderToHost ?? true,
      sendFollowUpEnabled: input.sendFollowUpEnabled ?? true,
      sendPostMeetingSurvey: input.sendPostMeetingSurvey ?? true,
    };

    if (input.smtpPassword) {
      data.smtpPassEnc = encrypt(input.smtpPassword);
    } else if (existing?.smtpPassEnc && input.provider !== 'platform') {
      data.smtpPassEnc = existing.smtpPassEnc;
    } else if (input.provider === 'platform') {
      data.smtpPassEnc = null;
    }

    if (input.sendFromUserEmail && input.provider === 'platform') {
      throw new AppError(
        'validation_error',
        400,
        'Choose an email provider (Gmail, Outlook, etc.) to send from your own address.',
      );
    }

    try {
      return await this.settingsRepo.upsert(userId, data);
    } catch (err) {
      const code =
        (err as { code?: string }).code ??
        (err as { driverError?: { code?: string } }).driverError?.code;
      if (code === 'ER_LOCK_WAIT_TIMEOUT') {
        throw new AppError(
          'conflict',
          409,
          'Could not save email settings because the database is busy. Please try again.',
        );
      }
      throw err;
    }
  }

  async sendTestEmail(userId: string, toEmail?: string) {
    const user = await this.userRepo.findById(userId);
    if (!user) throw new AppError('not_found', 404, 'User not found.');

    const settings = await this.getForUser(userId);
    const from = this.resolveFrom(settings, user.email, user.fullName);

    await this.sendMail(settings, user.email, user.fullName, {
      from,
      to: toEmail ?? user.email,
      subject: 'Calendar — test email configuration',
      html: `
        <p>Hi ${user.fullName},</p>
        <p>This is a test message confirming your email notification settings are working.</p>
        <p>Your guests will receive professional meeting reminders and follow-ups from this mailbox when enabled.</p>
      `,
    });
  }

  /** Unified send: platform uses SMTP or Gmail API; host custom SMTP when configured. */
  async sendMail(
    settings: UserEmailSettings | null,
    userEmail: string,
    userName: string,
    options: SendMailOptions,
  ): Promise<void> {
    if (
      settings?.sendFromUserEmail &&
      settings.provider !== 'platform' &&
      settings.smtpHost &&
      settings.smtpUser &&
      settings.smtpPassEnc
    ) {
      await new SmtpMailSender(
        settings.smtpHost,
        settings.smtpPort,
        settings.smtpUser,
        decrypt(settings.smtpPassEnc),
      ).send(options);
      return;
    }

    await createPlatformMailSender().send(options);
  }

  createTransporter(settings: UserEmailSettings | null, userEmail: string, userName: string) {
    if (
      settings?.sendFromUserEmail &&
      settings.provider !== 'platform' &&
      settings.smtpHost &&
      settings.smtpUser &&
      settings.smtpPassEnc
    ) {
      return nodemailer.createTransport({
        host: settings.smtpHost,
        port: settings.smtpPort,
        secure: settings.smtpPort === 465,
        auth: {
          user: settings.smtpUser,
          pass: decrypt(settings.smtpPassEnc),
        },
      });
    }

    return nodemailer.createTransport({
      host: env.SMTP_HOST,
      port: env.SMTP_PORT,
      secure: false,
      auth: env.SMTP_USER && env.SMTP_PASS ? { user: env.SMTP_USER, pass: env.SMTP_PASS } : undefined,
    });
  }

  resolveFrom(settings: UserEmailSettings | null, userEmail: string, userName: string): string {
    if (settings?.sendFromUserEmail && settings.fromEmail) {
      return settings.fromName
        ? `"${settings.fromName}" <${settings.fromEmail}>`
        : settings.fromEmail;
    }
    if (env.MAIL_TRANSPORT === 'gmail_api' && env.GMAIL_USER) {
      if (settings?.fromName) {
        return `"${settings.fromName}" <${env.GMAIL_USER}>`;
      }
      return env.GMAIL_USER;
    }
    if (settings?.fromName) {
      return `"${settings.fromName}" <${env.EMAIL_FROM}>`;
    }
    return env.EMAIL_FROM;
  }

  isNotificationsEnabled(settings: UserEmailSettings | null): boolean {
    return settings?.notificationsEnabled !== false;
  }

  isMailConfigured(): boolean {
    return isPlatformMailConfigured();
  }
}
