/**
 * Unit test for MailerCLientModule wiring.
 *
 * Tests what is realistically unit-testable (per design D5):
 * (a) Disabled path: forRootAsync() returns a bare module with no imports when MAILER_ENABLED !== 'true'.
 * (b) Enabled path: forRootAsync() includes imports (MailerModule + I18nModule).
 * (c) Adapter type: the useFactory builds a config whose template.adapter is a MjmlAdapter instance.
 * (d) t helper: the t helper is a callable function that delegates to I18nService.t.
 *
 * Strategy: mock @nestjs-modules/mailer and nestjs-i18n so their module bootstrapping
 * does NOT run. We intercept the useFactory from the MailerModule.forRootAsync call
 * to test the adapter config in isolation.
 */

import { MjmlAdapter } from '@nestjs-modules/mailer/adapters/mjml.adapter';
import { DynamicModule } from '@nestjs/common';

// Capture the factory that the module registers via MailerModule.forRootAsync.
// We set this variable inside the mock so the test can call the factory directly.
let capturedFactory: ((...args: unknown[]) => unknown) | undefined;

jest.mock('@nestjs-modules/mailer', () => {
  return {
    MailerModule: {
      forRootAsync: jest.fn((opts: { useFactory?: (...args: unknown[]) => unknown }) => {
        if (opts?.useFactory) {
          capturedFactory = opts.useFactory;
        }
        return { module: class MockMailerModule {}, providers: [] };
      }),
    },
  };
});

jest.mock('nestjs-i18n', () => {
  const AcceptLanguageResolver = class {};
  const QueryResolver = class {};
  const I18nModule = {
    forRoot: jest.fn(() => ({ module: class MockI18nModule {} })),
  };
  return { AcceptLanguageResolver, QueryResolver, I18nModule, I18nService: class {} };
});

jest.mock('@config/mailer', () => ({
  MailerTransportModule: class MockMailerTransportModule {},
}));

// Import the module under test AFTER the mocks are registered by jest.mock() (which is hoisted).

const { MailerCLientModule } = require('./mailer-client.module') as {
  MailerCLientModule: { forRootAsync: () => DynamicModule };
};

describe('MailerCLientModule.forRootAsync()', () => {
  const ORIGINAL_MAILER_ENABLED = process.env.MAILER_ENABLED;

  beforeEach(() => {
    capturedFactory = undefined;
    jest.clearAllMocks();
  });

  afterAll(() => {
    if (ORIGINAL_MAILER_ENABLED === undefined) {
      delete process.env.MAILER_ENABLED;
    } else {
      process.env.MAILER_ENABLED = ORIGINAL_MAILER_ENABLED;
    }
  });

  // ─── Disabled path ──────────────────────────────────────────────────────────

  describe('disabled path (MAILER_ENABLED !== "true")', () => {
    it('returns a bare dynamic module with no imports when MAILER_ENABLED is unset', () => {
      delete process.env.MAILER_ENABLED;

      const mod = MailerCLientModule.forRootAsync();

      expect(mod.module).toBe(MailerCLientModule);
      expect(mod.imports).toBeUndefined();
    });

    it('returns a bare dynamic module with no imports when MAILER_ENABLED is "false"', () => {
      process.env.MAILER_ENABLED = 'false';

      const mod = MailerCLientModule.forRootAsync();

      expect(mod.module).toBe(MailerCLientModule);
      expect(mod.imports).toBeUndefined();
    });
  });

  // ─── Enabled path ───────────────────────────────────────────────────────────

  describe('enabled path (MAILER_ENABLED === "true")', () => {
    beforeEach(() => {
      process.env.MAILER_ENABLED = 'true';
    });

    afterEach(() => {
      delete process.env.MAILER_ENABLED;
    });

    it('returns a dynamic module with imports when the mailer is enabled', () => {
      const mod = MailerCLientModule.forRootAsync();

      expect(mod.module).toBe(MailerCLientModule);
      expect(Array.isArray(mod.imports)).toBe(true);
      expect((mod.imports as unknown[]).length).toBeGreaterThan(0);
    });

    it('configures the template adapter as an instance of MjmlAdapter, not HandlebarsAdapter', () => {
      MailerCLientModule.forRootAsync(); // triggers capturedFactory capture via the mock

      expect(capturedFactory).toBeDefined();

      const mockConfigService = { get: jest.fn().mockReturnValue('noreply@example.com') };
      const mockI18nService = { t: jest.fn((key: string) => key) };
      const mockMailerTransportService = {
        getTransport: jest.fn().mockReturnValue({ host: 'smtp.example.com' }),
      };

      const config = capturedFactory(
        mockConfigService,
        mockI18nService,
        mockMailerTransportService,
      ) as { template: { adapter: unknown } };

      const adapter = config?.template?.adapter;

      // (b) The adapter MUST be an instance of MjmlAdapter, NOT a bare HandlebarsAdapter.
      expect(adapter).toBeInstanceOf(MjmlAdapter);
    });

    it('invokes the wired t helper (backed by I18nService) when a template renders {{t}}', (done) => {
      MailerCLientModule.forRootAsync();

      expect(capturedFactory).toBeDefined();

      const i18nSpy = jest.fn((_key: string, _opts?: unknown) => 'TRANSLATED');
      const config = capturedFactory(
        { get: jest.fn().mockReturnValue('noreply@example.com') },
        { t: i18nSpy },
        { getTransport: jest.fn().mockReturnValue({ host: 'smtp.example.com' }) },
      ) as { template: { adapter: InstanceType<typeof MjmlAdapter>; dir: string } };

      const mail: { data: { template: string; context: Record<string, unknown>; html?: string } } =
        {
          data: {
            template: 'forgot-password.mjml',
            context: {
              username: 'alice',
              email: 'alice@example.com',
              resetLink: 'https://x.example.com/reset',
              validHours: 24,
            },
          },
        };

      config.template.adapter.compile(
        mail,
        (err?: unknown) => {
          try {
            expect(err).toBeFalsy();
            // The wired i18nSpy (the REAL I18nService mock) must have been invoked by the template.
            expect(i18nSpy).toHaveBeenCalledWith(
              'iam.PasswordRecovery',
              expect.objectContaining({ lang: 'en' }),
            );
            done();
          } catch (e) {
            done(e as Error);
          }
        },
        { template: { dir: config.template.dir } },
      );
    });
  });
});
