import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Brevo, BrevoClient } from '@getbrevo/brevo';
import Handlebars from 'handlebars';
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { ServiceLogger } from '../../common/logging/service-logger';

import type {
  EmailSendResult,
  PasswordSetupEmailRecipient,
  TransactionalEmailInput,
  WelcomeEmailRecipient,
} from './email.types';

@Injectable()
export class EmailService {
  private readonly logger = new ServiceLogger(EmailService.name);
  private client?: BrevoClient;
  private welcomeTemplate?: Handlebars.TemplateDelegate;
  private passwordSetupTemplate?: Handlebars.TemplateDelegate;

  constructor(private readonly configService: ConfigService) {}

  /**
   * Sends through Brevo only when EMAIL_ENABLED=true. It is intentionally not
   * called by any existing application flow yet.
   */
  async sendTransactionalEmail(
    input: TransactionalEmailInput,
  ): Promise<EmailSendResult> {
    if (!this.isEmailEnabled()) {
      return { status: 'skipped', reason: 'email_disabled' };
    }

    this.assertValidInput(input);

    const startedAt = Date.now();
    try {
      const result =
        await this.getClient().transactionalEmails.sendTransacEmail(
          this.toBrevoRequest(input),
          {
            timeoutInSeconds: 30,
            maxRetries: 2,
          },
        );

      const response: EmailSendResult = {
        status: 'sent',
        messageId: result.messageId,
        messageIds: result.messageIds,
      };
      this.logger.log('email.transactional.sent', {
        recipientCount: input.to.length,
        messageId: result.messageId,
        durationMs: Date.now() - startedAt,
      });
      return response;
    } catch (error) {
      this.logger.error('email.transactional.failed', error, {
        recipientCount: input.to.length,
        durationMs: Date.now() - startedAt,
      });
      return { status: 'failed', reason: 'provider_error' };
    }
  }

  async sendWelcomeEmail(
    recipient: WelcomeEmailRecipient,
  ): Promise<EmailSendResult> {
    if (!this.isEmailEnabled()) {
      return { status: 'skipped', reason: 'email_disabled' };
    }

    const htmlContent = await this.renderWelcomeTemplate({
      name: recipient.name ?? recipient.email,
      logoUrl: this.getPublicLogoUrl(),
    });

    return this.sendTransactionalEmail({
      to: [recipient],
      subject: 'Welcome to IIH SEO Portal',
      htmlContent,
      textContent: [
        `Hello ${recipient.name ?? recipient.email},`,
        '',
        'Welcome to IIH SEO Portal. An administrator has created your account.',
        'You can now sign in with your registered email address and the password provided by your administrator.',
        '',
        'If you were not expecting this account, please contact your administrator.',
      ].join('\n'),
      tags: ['user-welcome'],
    });
  }

  async sendPasswordSetupEmail(input: {
    recipient: PasswordSetupEmailRecipient;
    setupUrl: string;
    expiresInHours: number;
  }): Promise<EmailSendResult> {
    if (!this.isEmailEnabled()) {
      return { status: 'skipped', reason: 'email_disabled' };
    }

    const htmlContent = await this.renderPasswordSetupTemplate({
      name: input.recipient.name ?? input.recipient.email,
      logoUrl: this.getPublicLogoUrl(),
      setupUrl: input.setupUrl,
      expiresInHours: input.expiresInHours,
    });

    return this.sendTransactionalEmail({
      to: [input.recipient],
      subject: 'Set up your IIH SEO Portal account',
      htmlContent,
      textContent: [
        `Hello ${input.recipient.name ?? input.recipient.email},`,
        '',
        'Your IIH SEO Portal account is ready. Set a password to verify and activate your account:',
        input.setupUrl,
        '',
        `This link expires in ${input.expiresInHours} hours and can only be used once.`,
      ].join('\n'),
      tags: ['user-password-setup'],
    });
  }

  private isEmailEnabled() {
    return (
      this.configService.get<string>('EMAIL_ENABLED')?.trim().toLowerCase() ===
      'true'
    );
  }

  private getClient() {
    if (!this.client) {
      this.client = new BrevoClient({
        apiKey: this.configService.getOrThrow<string>('BREVO_API_KEY'),
        timeoutInSeconds: 30,
        maxRetries: 2,
      });
    }

    return this.client;
  }

  private async renderWelcomeTemplate(data: { name: string; logoUrl: string }) {
    if (!this.welcomeTemplate) {
      const templatePath = join(__dirname, 'templates', 'welcome-user.hbs');
      const source = await readFile(templatePath, 'utf8');
      this.welcomeTemplate = Handlebars.compile(source, {
        noEscape: false,
        strict: true,
      });
    }

    return this.welcomeTemplate(data);
  }

  private async renderPasswordSetupTemplate(data: {
    name: string;
    logoUrl: string;
    setupUrl: string;
    expiresInHours: number;
  }) {
    if (!this.passwordSetupTemplate) {
      const templatePath = join(__dirname, 'templates', 'set-password.hbs');
      const source = await readFile(templatePath, 'utf8');
      this.passwordSetupTemplate = Handlebars.compile(source, {
        noEscape: false,
        strict: true,
      });
    }

    return this.passwordSetupTemplate(data);
  }

  private getPublicLogoUrl() {
    const baseUrl = this.configService
      .getOrThrow<string>('API_PUBLIC_URL')
      .replace(/\/+$/, '');
    return `${baseUrl}/public/icon-logo.svg`;
  }

  private toBrevoRequest(
    input: TransactionalEmailInput,
  ): Brevo.SendTransacEmailRequest {
    const fromEmail =
      this.configService.getOrThrow<string>('EMAIL_FROM_ADDRESS');
    const fromName = this.configService.get<string>('EMAIL_FROM_NAME')?.trim();
    const configuredReplyTo = this.configService
      .get<string>('EMAIL_REPLY_TO')
      ?.trim();

    return {
      sender: {
        email: fromEmail,
        ...(fromName ? { name: fromName } : {}),
      },
      to: input.to,
      ...(input.subject ? { subject: input.subject } : {}),
      ...(input.htmlContent ? { htmlContent: input.htmlContent } : {}),
      ...(input.textContent ? { textContent: input.textContent } : {}),
      ...(input.templateId ? { templateId: input.templateId } : {}),
      ...(input.params ? { params: input.params } : {}),
      replyTo:
        input.replyTo ??
        (configuredReplyTo ? { email: configuredReplyTo } : undefined),
      ...(input.tags?.length ? { tags: input.tags } : {}),
    };
  }

  private assertValidInput(input: TransactionalEmailInput) {
    if (input.to.length === 0) {
      throw new Error('At least one email recipient is required.');
    }

    if (!input.templateId && !input.subject?.trim()) {
      throw new Error('A subject is required when no Brevo template is used.');
    }

    if (
      !input.templateId &&
      !input.htmlContent?.trim() &&
      !input.textContent?.trim()
    ) {
      throw new Error(
        'HTML or plain-text content is required when no Brevo template is used.',
      );
    }
  }
}
