// Mock dependencies
jest.mock('../../../../../services/email/core/EmailClient', () => {
  return jest.fn().mockImplementation(() => ({
    initialize: jest.fn().mockResolvedValue(),
    sendEmail: jest.fn().mockResolvedValue({ success: true, messageId: 'msg-123' }),
  }));
});

jest.mock('../../../../../services/email/core/TemplateManager', () => {
  return jest.fn().mockImplementation(() => ({
    initialize: jest.fn().mockResolvedValue(),
    renderTemplate: jest.fn().mockResolvedValue('<html>Test</html>'),
  }));
});

jest.mock('../../../../../utils/logger', () => ({
  info: jest.fn(),
  error: jest.fn(),
  warn: jest.fn(),
}));

const RentalFlowEmailService = require('../../../../../services/email/domain/RentalFlowEmailService');

describe('RentalFlowEmailService', () => {
  let service;
  const originalEnv = process.env;

  beforeEach(() => {
    jest.clearAllMocks();
    process.env = { ...originalEnv, FRONTEND_URL: 'http://localhost:3000' };
    service = new RentalFlowEmailService();
  });

  afterEach(() => {
    process.env = originalEnv;
  });

  describe('initialize', () => {
    it('should initialize only once', async () => {
      await service.initialize();
      await service.initialize();

      expect(service.emailClient.initialize).toHaveBeenCalledTimes(1);
      expect(service.templateManager.initialize).toHaveBeenCalledTimes(1);
    });
  });

  describe('sendRentalRequestEmail', () => {
    const owner = { firstName: 'John', email: 'john@example.com' };
    const renter = { firstName: 'Jane', lastName: 'Smith' };
    const rental = {
      id: 123,
      item: { name: 'Power Drill' },
      startDateTime: '2024-01-15T10:00:00Z',
      endDateTime: '2024-01-16T10:00:00Z',
      totalAmount: '50.00',
      payoutAmount: '45.00',
      deliveryMethod: 'pickup',
      intendedUse: 'Home project',
    };

    it('should send rental request email with correct variables', async () => {
      const result = await service.sendRentalRequestEmail(owner, renter, rental);

      expect(result.success).toBe(true);
      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalRequestToOwner',
        expect.objectContaining({
          ownerName: 'John',
          renterName: 'Jane Smith',
          itemName: 'Power Drill',
          totalAmount: '50.00',
          payoutAmount: '45.00',
          deliveryMethod: 'pickup',
          intendedUse: 'Home project',
          approveUrl: 'http://localhost:3000/owning?rentalId=123',
        })
      );
      expect(service.emailClient.sendEmail).toHaveBeenCalledWith(
        'john@example.com',
        'Rental Request for Power Drill',
        expect.any(String)
      );
    });

    it('should use default values when data is missing', async () => {
      const minimalRental = { id: 123, item: null };
      const minimalRenter = {};

      await service.sendRentalRequestEmail(owner, minimalRenter, minimalRental);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalRequestToOwner',
        expect.objectContaining({
          itemName: 'your item',
          startDate: 'Not specified',
          endDate: 'Not specified',
          totalAmount: '0.00',
          payoutAmount: '0.00',
          deliveryMethod: 'Not specified',
          intendedUse: 'Not specified',
        })
      );
    });

    it('should use A renter fallback when names produce empty string', async () => {
      const minimalRental = { id: 123, item: null };
      const renterWithEmptyNames = { firstName: '', lastName: '' };

      await service.sendRentalRequestEmail(owner, renterWithEmptyNames, minimalRental);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalRequestToOwner',
        expect.objectContaining({
          renterName: 'A renter',
        })
      );
    });

    it('should handle errors gracefully', async () => {
      service.templateManager.renderTemplate.mockRejectedValueOnce(new Error('Template error'));

      const result = await service.sendRentalRequestEmail(owner, renter, rental);

      expect(result.success).toBe(false);
      expect(result.error).toBe('Template error');
    });
  });

  describe('sendRentalRequestConfirmationEmail', () => {
    const renter = { firstName: 'Jane', email: 'jane@example.com' };
    const rental = {
      item: { name: 'Power Drill' },
      startDateTime: '2024-01-15T10:00:00Z',
      endDateTime: '2024-01-16T10:00:00Z',
      totalAmount: '50.00',
      deliveryMethod: 'pickup',
    };

    it('should send rental request confirmation with correct variables', async () => {
      const result = await service.sendRentalRequestConfirmationEmail(renter, rental);

      expect(result.success).toBe(true);
      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalRequestConfirmationToRenter',
        expect.objectContaining({
          renterName: 'Jane',
          itemName: 'Power Drill',
          totalAmount: '50.00',
          deliveryMethod: 'pickup',
          viewRentalsUrl: 'http://localhost:3000/renting',
        })
      );
      expect(service.emailClient.sendEmail).toHaveBeenCalledWith(
        'jane@example.com',
        'Rental Request Submitted - Power Drill',
        expect.any(String)
      );
    });

    it('should use default name when firstName is missing', async () => {
      const renterNoName = { email: 'jane@example.com' };

      await service.sendRentalRequestConfirmationEmail(renterNoName, rental);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalRequestConfirmationToRenter',
        expect.objectContaining({ renterName: 'there' })
      );
    });

    it('should show different payment message for free rentals', async () => {
      const freeRental = { ...rental, totalAmount: '0' };

      await service.sendRentalRequestConfirmationEmail(renter, freeRental);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalRequestConfirmationToRenter',
        expect.objectContaining({
          paymentMessage: 'The owner will review your request and respond soon.',
        })
      );
    });

    it('should show payment pending message for paid rentals', async () => {
      await service.sendRentalRequestConfirmationEmail(renter, rental);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalRequestConfirmationToRenter',
        expect.objectContaining({
          paymentMessage: "The owner will review your request. You'll only be charged if they approve it.",
        })
      );
    });

    it('should handle errors gracefully', async () => {
      service.emailClient.sendEmail.mockRejectedValueOnce(new Error('Send error'));

      const result = await service.sendRentalRequestConfirmationEmail(renter, rental);

      expect(result.success).toBe(false);
      expect(result.error).toBe('Send error');
    });
  });

  describe('sendRentalApprovalConfirmationEmail', () => {
    const owner = {
      firstName: 'John',
      email: 'john@example.com',
      stripeConnectedAccountId: 'acct_123',
    };
    const renter = { firstName: 'Jane', lastName: 'Smith' };
    const rental = {
      id: 123,
      item: { name: 'Power Drill' },
      startDateTime: '2024-01-15T10:00:00Z',
      endDateTime: '2024-01-16T10:00:00Z',
      deliveryMethod: 'delivery',
      totalAmount: '50.00',
      payoutAmount: '45.00',
      platformFee: '5.00',
    };

    it('should send approval confirmation with correct variables', async () => {
      const result = await service.sendRentalApprovalConfirmationEmail(owner, renter, rental);

      expect(result.success).toBe(true);
      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalApprovalConfirmationToOwner',
        expect.objectContaining({
          ownerName: 'John',
          itemName: 'Power Drill',
          renterName: 'Jane Smith',
          deliveryMethod: 'Delivery',
          paymentMessage: 'their payment has been processed successfully.',
          rentalDetailsUrl: 'http://localhost:3000/owning?rentalId=123',
        })
      );
      expect(service.emailClient.sendEmail).toHaveBeenCalledWith(
        'john@example.com',
        'Rental Approved - Power Drill',
        expect.any(String)
      );
    });

    it('should show stripe setup reminder when no stripe account for paid rental', async () => {
      const ownerNoStripe = { firstName: 'John', email: 'john@example.com' };

      await service.sendRentalApprovalConfirmationEmail(ownerNoStripe, renter, rental);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalApprovalConfirmationToOwner',
        expect.objectContaining({
          stripeSection: expect.stringContaining('Action Required'),
          stripeSection: expect.stringContaining('Set Up Your Earnings Account'),
        })
      );
    });

    it('should show earnings active message when stripe account exists for paid rental', async () => {
      await service.sendRentalApprovalConfirmationEmail(owner, renter, rental);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalApprovalConfirmationToOwner',
        expect.objectContaining({
          stripeSection: expect.stringContaining('Earnings Account Active'),
        })
      );
    });

    it('should handle free rentals correctly', async () => {
      const freeRental = { ...rental, totalAmount: '0', payoutAmount: '0', platformFee: '0' };

      await service.sendRentalApprovalConfirmationEmail(owner, renter, freeRental);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalApprovalConfirmationToOwner',
        expect.objectContaining({
          paymentMessage: 'this is a free rental (no payment required).',
          earningsSection: '',
          stripeSection: '',
        })
      );
    });

    it('should use default name when firstName is missing', async () => {
      const ownerNoName = { email: 'john@example.com', stripeConnectedAccountId: 'acct_123' };

      await service.sendRentalApprovalConfirmationEmail(ownerNoName, renter, rental);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalApprovalConfirmationToOwner',
        expect.objectContaining({ ownerName: 'there' })
      );
    });

    it('should handle errors gracefully', async () => {
      service.templateManager.renderTemplate.mockRejectedValueOnce(new Error('Template error'));

      const result = await service.sendRentalApprovalConfirmationEmail(owner, renter, rental);

      expect(result.success).toBe(false);
      expect(result.error).toBe('Template error');
    });
  });

  describe('sendRentalDeclinedEmail', () => {
    const renter = { firstName: 'Jane', email: 'jane@example.com' };
    const rental = {
      item: { name: 'Power Drill' },
      startDateTime: '2024-01-15T10:00:00Z',
      endDateTime: '2024-01-16T10:00:00Z',
      totalAmount: '50.00',
      payoutAmount: '45.00',
      deliveryMethod: 'pickup',
    };

    it('should send declined email with correct variables', async () => {
      const result = await service.sendRentalDeclinedEmail(renter, rental, 'Item unavailable');

      expect(result.success).toBe(true);
      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalDeclinedToRenter',
        expect.objectContaining({
          renterName: 'Jane',
          itemName: 'Power Drill',
          deliveryMethod: 'pickup',
          browseItemsUrl: 'http://localhost:3000/',
        })
      );
      expect(service.emailClient.sendEmail).toHaveBeenCalledWith(
        'jane@example.com',
        'Rental Request Declined - Power Drill',
        expect.any(String)
      );
    });

    it('should include owner message when decline reason is provided', async () => {
      await service.sendRentalDeclinedEmail(renter, rental, 'Item is broken');

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalDeclinedToRenter',
        expect.objectContaining({
          ownerMessage: expect.stringContaining('Item is broken'),
        })
      );
    });

    it('should not include owner message when no decline reason', async () => {
      await service.sendRentalDeclinedEmail(renter, rental, null);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalDeclinedToRenter',
        expect.objectContaining({
          ownerMessage: '',
        })
      );
    });

    it('should show no charge message for paid rentals', async () => {
      await service.sendRentalDeclinedEmail(renter, rental, null);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalDeclinedToRenter',
        expect.objectContaining({
          paymentMessage: 'Since your request was declined before payment was processed, you will not be charged.',
        })
      );
    });

    it('should show no payment message for free rentals', async () => {
      const freeRental = { ...rental, totalAmount: '0' };

      await service.sendRentalDeclinedEmail(renter, freeRental, null);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalDeclinedToRenter',
        expect.objectContaining({
          paymentMessage: 'No payment was required for this rental request.',
        })
      );
    });

    it('should use default name when firstName is missing', async () => {
      const renterNoName = { email: 'jane@example.com' };

      await service.sendRentalDeclinedEmail(renterNoName, rental, null);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalDeclinedToRenter',
        expect.objectContaining({ renterName: 'there' })
      );
    });

    it('should handle errors gracefully', async () => {
      service.emailClient.sendEmail.mockRejectedValueOnce(new Error('Send error'));

      const result = await service.sendRentalDeclinedEmail(renter, rental, null);

      expect(result.success).toBe(false);
      expect(result.error).toBe('Send error');
    });
  });

  describe('sendRentalConfirmation', () => {
    const userEmail = 'jane@example.com';
    const notification = {
      title: 'Rental Confirmed',
      message: 'Your rental has been confirmed.',
    };
    const rental = {
      item: { name: 'Power Drill' },
      startDateTime: '2024-01-15T10:00:00Z',
      endDateTime: '2024-01-16T10:00:00Z',
      totalAmount: '50.00',
      paymentStatus: 'paid',
      paymentMethodBrand: 'visa',
      paymentMethodLast4: '4242',
      stripePaymentIntentId: 'pi_123',
      chargedAt: '2024-01-15T09:00:00Z',
    };

    it('should send rental confirmation with correct variables', async () => {
      const result = await service.sendRentalConfirmation(
        userEmail,
        notification,
        rental,
        'Jane',
        true
      );

      expect(result.success).toBe(true);
      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalConfirmationToUser',
        expect.objectContaining({
          recipientName: 'Jane',
          title: 'Rental Confirmed',
          message: 'Your rental has been confirmed.',
          itemName: 'Power Drill',
          isRenter: true,
        })
      );
      expect(service.emailClient.sendEmail).toHaveBeenCalledWith(
        'jane@example.com',
        'Rental Confirmation - Power Drill',
        expect.any(String)
      );
    });

    it('should include payment receipt for renter with paid rental', async () => {
      await service.sendRentalConfirmation(userEmail, notification, rental, 'Jane', true);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalConfirmationToUser',
        expect.objectContaining({
          paymentSection: expect.stringContaining('Payment Receipt'),
          paymentSection: expect.stringContaining('Visa ending in 4242'),
        })
      );
    });

    it('should show free rental message for zero amount', async () => {
      const freeRental = { ...rental, totalAmount: '0' };

      await service.sendRentalConfirmation(userEmail, notification, freeRental, 'Jane', true);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalConfirmationToUser',
        expect.objectContaining({
          paymentSection: expect.stringContaining('No Payment Required'),
        })
      );
    });

    it('should not include payment section for owner', async () => {
      await service.sendRentalConfirmation(userEmail, notification, rental, 'John', false);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalConfirmationToUser',
        expect.objectContaining({
          paymentSection: '',
          isRenter: false,
        })
      );
    });

    it('should use default name when recipientName is null', async () => {
      await service.sendRentalConfirmation(userEmail, notification, rental, null, true);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalConfirmationToUser',
        expect.objectContaining({ recipientName: 'there' })
      );
    });

    it('should handle missing rental data', async () => {
      const minimalRental = {};

      await service.sendRentalConfirmation(userEmail, notification, minimalRental, 'Jane', true);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalConfirmationToUser',
        expect.objectContaining({
          itemName: 'Unknown Item',
          startDate: 'Not specified',
          endDate: 'Not specified',
        })
      );
    });

    it('should handle errors gracefully', async () => {
      service.templateManager.renderTemplate.mockRejectedValueOnce(new Error('Template error'));

      const result = await service.sendRentalConfirmation(
        userEmail,
        notification,
        rental,
        'Jane',
        true
      );

      expect(result.success).toBe(false);
      expect(result.error).toBe('Template error');
    });
  });

  describe('sendRentalConfirmationEmails', () => {
    const owner = { firstName: 'John', email: 'john@example.com' };
    const renter = { firstName: 'Jane', email: 'jane@example.com' };
    const rental = {
      id: 123,
      ownerId: 1,
      renterId: 2,
      item: { name: 'Power Drill' },
      startDateTime: '2024-01-15T10:00:00Z',
      totalAmount: '50.00',
      paymentStatus: 'paid',
    };

    it('should send confirmation emails to both owner and renter', async () => {
      const result = await service.sendRentalConfirmationEmails(owner, renter, rental);

      expect(result.ownerEmailSent).toBe(true);
      expect(result.renterEmailSent).toBe(true);
      expect(service.emailClient.sendEmail).toHaveBeenCalledTimes(2);
    });

    it('should send owner email with isRenter false', async () => {
      await service.sendRentalConfirmationEmails(owner, renter, rental);

      // First call is for owner
      const firstCall = service.templateManager.renderTemplate.mock.calls[0];
      expect(firstCall[1]).toMatchObject({ isRenter: false });
    });

    it('should send renter email with isRenter true', async () => {
      await service.sendRentalConfirmationEmails(owner, renter, rental);

      // Second call is for renter
      const secondCall = service.templateManager.renderTemplate.mock.calls[1];
      expect(secondCall[1]).toMatchObject({ isRenter: true });
    });

    it('should handle missing owner email', async () => {
      const ownerNoEmail = { firstName: 'John' };

      const result = await service.sendRentalConfirmationEmails(ownerNoEmail, renter, rental);

      expect(result.ownerEmailSent).toBe(false);
      expect(result.renterEmailSent).toBe(true);
    });

    it('should handle missing renter email', async () => {
      const renterNoEmail = { firstName: 'Jane' };

      const result = await service.sendRentalConfirmationEmails(owner, renterNoEmail, rental);

      expect(result.ownerEmailSent).toBe(true);
      expect(result.renterEmailSent).toBe(false);
    });

    it('should continue sending renter email if owner email fails', async () => {
      service.emailClient.sendEmail
        .mockResolvedValueOnce({ success: false, error: 'Owner send failed' })
        .mockResolvedValueOnce({ success: true, messageId: 'msg-456' });

      const result = await service.sendRentalConfirmationEmails(owner, renter, rental);

      expect(result.ownerEmailSent).toBe(false);
      expect(result.renterEmailSent).toBe(true);
    });

    it('should handle errors in owner email without crashing', async () => {
      service.emailClient.sendEmail
        .mockRejectedValueOnce(new Error('Network error'))
        .mockResolvedValueOnce({ success: true, messageId: 'msg-456' });

      const result = await service.sendRentalConfirmationEmails(owner, renter, rental);

      expect(result.ownerEmailSent).toBe(false);
      expect(result.renterEmailSent).toBe(true);
    });
  });

  describe('sendRentalCancellationEmails', () => {
    const owner = { firstName: 'John', email: 'john@example.com' };
    const renter = { firstName: 'Jane', email: 'jane@example.com' };
    const rental = {
      item: { name: 'Power Drill' },
      startDateTime: '2024-01-15T10:00:00Z',
      endDateTime: '2024-01-16T10:00:00Z',
      cancelledBy: 'renter',
      cancelledAt: '2024-01-14T10:00:00Z',
      totalAmount: '50.00',
    };
    const refundInfo = {
      amount: 50,
      percentage: 1,
      reason: 'Full refund - cancelled before rental start',
    };

    it('should send cancellation emails when renter cancels', async () => {
      const result = await service.sendRentalCancellationEmails(owner, renter, rental, refundInfo);

      expect(result.confirmationEmailSent).toBe(true);
      expect(result.notificationEmailSent).toBe(true);
      expect(service.emailClient.sendEmail).toHaveBeenCalledTimes(2);
    });

    it('should send confirmation to renter and notification to owner when renter cancels', async () => {
      await service.sendRentalCancellationEmails(owner, renter, rental, refundInfo);

      // Confirmation to renter (canceller)
      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalCancellationConfirmationToUser',
        expect.objectContaining({ recipientName: 'Jane' })
      );
      // Notification to owner
      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalCancellationNotificationToUser',
        expect.objectContaining({ recipientName: 'John' })
      );
    });

    it('should send confirmation to owner and notification to renter when owner cancels', async () => {
      const ownerCancelledRental = { ...rental, cancelledBy: 'owner' };

      await service.sendRentalCancellationEmails(owner, renter, ownerCancelledRental, refundInfo);

      // Confirmation to owner (canceller)
      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalCancellationConfirmationToUser',
        expect.objectContaining({ recipientName: 'John' })
      );
      // Notification to renter
      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalCancellationNotificationToUser',
        expect.objectContaining({ recipientName: 'Jane' })
      );
    });

    it('should include refund section for paid rentals', async () => {
      await service.sendRentalCancellationEmails(owner, renter, rental, refundInfo);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalCancellationConfirmationToUser',
        expect.objectContaining({
          refundSection: expect.stringContaining('Refund Information'),
        })
      );
    });

    it('should show no refund message when refund amount is zero', async () => {
      const noRefundInfo = { amount: 0, percentage: 0, reason: 'Cancelled after rental period' };

      await service.sendRentalCancellationEmails(owner, renter, rental, noRefundInfo);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalCancellationConfirmationToUser',
        expect.objectContaining({
          refundSection: expect.stringContaining('No Refund Available'),
        })
      );
    });

    it('should not include refund section for free rentals', async () => {
      const freeRental = { ...rental, totalAmount: '0' };

      await service.sendRentalCancellationEmails(owner, renter, freeRental, refundInfo);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalCancellationConfirmationToUser',
        expect.objectContaining({
          refundSection: '',
        })
      );
    });

    it('should use default name when firstName is missing', async () => {
      const ownerNoName = { email: 'john@example.com' };
      const renterNoName = { email: 'jane@example.com' };

      await service.sendRentalCancellationEmails(ownerNoName, renterNoName, rental, refundInfo);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalCancellationConfirmationToUser',
        expect.objectContaining({ recipientName: 'there' })
      );
    });

    it('should continue if confirmation email fails', async () => {
      service.emailClient.sendEmail
        .mockResolvedValueOnce({ success: false, error: 'Send failed' })
        .mockResolvedValueOnce({ success: true, messageId: 'msg-456' });

      const result = await service.sendRentalCancellationEmails(owner, renter, rental, refundInfo);

      expect(result.confirmationEmailSent).toBe(false);
      expect(result.notificationEmailSent).toBe(true);
    });
  });

  describe('sendRentalCompletionEmails', () => {
    const owner = {
      firstName: 'John',
      lastName: 'Doe',
      email: 'john@example.com',
      stripeConnectedAccountId: 'acct_123',
    };
    const renter = { firstName: 'Jane', lastName: 'Smith', email: 'jane@example.com' };
    const rental = {
      id: 123,
      item: { name: 'Power Drill' },
      startDateTime: '2024-01-15T10:00:00Z',
      endDateTime: '2024-01-16T10:00:00Z',
      actualReturnDateTime: '2024-01-16T09:30:00Z',
      itemReviewSubmittedAt: null,
      totalAmount: '50.00',
      payoutAmount: '45.00',
      platformFee: '5.00',
    };

    it('should send completion emails to both owner and renter', async () => {
      const result = await service.sendRentalCompletionEmails(owner, renter, rental);

      expect(result.ownerEmailSent).toBe(true);
      expect(result.renterEmailSent).toBe(true);
      expect(service.emailClient.sendEmail).toHaveBeenCalledTimes(2);
    });

    it('should send renter email with review prompt when not yet reviewed', async () => {
      await service.sendRentalCompletionEmails(owner, renter, rental);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalCompletionThankYouToRenter',
        expect.objectContaining({
          reviewSection: expect.stringContaining('Share Your Experience'),
          reviewSection: expect.stringContaining('Leave a Review'),
        })
      );
    });

    it('should show thank you message when review already submitted', async () => {
      const reviewedRental = { ...rental, itemReviewSubmittedAt: '2024-01-16T12:00:00Z' };

      await service.sendRentalCompletionEmails(owner, renter, reviewedRental);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalCompletionThankYouToRenter',
        expect.objectContaining({
          reviewSection: expect.stringContaining('Thank You for Your Review'),
        })
      );
    });

    it('should send owner email with earnings for paid rental', async () => {
      await service.sendRentalCompletionEmails(owner, renter, rental);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalCompletionCongratsToOwner',
        expect.objectContaining({
          earningsSection: expect.stringContaining('Your Earnings'),
          earningsSection: expect.stringContaining('$45.00'),
        })
      );
    });

    it('should show payout initiated message when owner has stripe account', async () => {
      await service.sendRentalCompletionEmails(owner, renter, rental);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalCompletionCongratsToOwner',
        expect.objectContaining({
          stripeSection: expect.stringContaining('Payout Initiated'),
        })
      );
    });

    it('should show stripe setup reminder when owner has no stripe account for paid rental', async () => {
      const ownerNoStripe = { firstName: 'John', lastName: 'Doe', email: 'john@example.com' };

      await service.sendRentalCompletionEmails(ownerNoStripe, renter, rental);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalCompletionCongratsToOwner',
        expect.objectContaining({
          stripeSection: expect.stringContaining('Action Required'),
        })
      );
    });

    it('should not show earnings section for free rentals', async () => {
      const freeRental = { ...rental, totalAmount: '0', payoutAmount: '0', platformFee: '0' };

      await service.sendRentalCompletionEmails(owner, renter, freeRental);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalCompletionCongratsToOwner',
        expect.objectContaining({
          earningsSection: '',
          stripeSection: '',
        })
      );
    });

    it('should use default name when firstName is missing', async () => {
      const ownerNoName = { email: 'john@example.com', stripeConnectedAccountId: 'acct_123' };
      const renterNoName = { email: 'jane@example.com' };

      await service.sendRentalCompletionEmails(ownerNoName, renterNoName, rental);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalCompletionThankYouToRenter',
        expect.objectContaining({ renterName: 'there' })
      );
      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'rentalCompletionCongratsToOwner',
        expect.objectContaining({ ownerName: 'there' })
      );
    });

    it('should continue if renter email fails', async () => {
      service.emailClient.sendEmail
        .mockResolvedValueOnce({ success: false, error: 'Send failed' })
        .mockResolvedValueOnce({ success: true, messageId: 'msg-456' });

      const result = await service.sendRentalCompletionEmails(owner, renter, rental);

      expect(result.renterEmailSent).toBe(false);
      expect(result.ownerEmailSent).toBe(true);
    });

    it('should handle errors in renter email without crashing', async () => {
      service.emailClient.sendEmail
        .mockRejectedValueOnce(new Error('Network error'))
        .mockResolvedValueOnce({ success: true, messageId: 'msg-456' });

      const result = await service.sendRentalCompletionEmails(owner, renter, rental);

      expect(result.renterEmailSent).toBe(false);
      expect(result.ownerEmailSent).toBe(true);
    });
  });

  describe('sendPayoutReceivedEmail', () => {
    const owner = { firstName: 'John', email: 'john@example.com' };
    const rental = {
      item: { name: 'Power Drill' },
      startDateTime: '2024-01-15T10:00:00Z',
      endDateTime: '2024-01-16T10:00:00Z',
      totalAmount: '50.00',
      platformFee: '5.00',
      payoutAmount: '45.00',
      stripeTransferId: 'tr_123',
    };

    it('should send payout email with correct variables', async () => {
      const result = await service.sendPayoutReceivedEmail(owner, rental);

      expect(result.success).toBe(true);
      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'payoutReceivedToOwner',
        expect.objectContaining({
          ownerName: 'John',
          itemName: 'Power Drill',
          totalAmount: '50.00',
          platformFee: '5.00',
          payoutAmount: '45.00',
          stripeTransferId: 'tr_123',
          earningsDashboardUrl: 'http://localhost:3000/earnings',
        })
      );
      expect(service.emailClient.sendEmail).toHaveBeenCalledWith(
        'john@example.com',
        'Earnings Received - $45.00 for Power Drill',
        expect.any(String)
      );
    });

    it('should use default name when firstName is missing', async () => {
      const ownerNoName = { email: 'john@example.com' };

      await service.sendPayoutReceivedEmail(ownerNoName, rental);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'payoutReceivedToOwner',
        expect.objectContaining({ ownerName: 'there' })
      );
    });

    it('should handle missing item name', async () => {
      const rentalNoItem = { ...rental, item: null };

      await service.sendPayoutReceivedEmail(owner, rentalNoItem);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'payoutReceivedToOwner',
        expect.objectContaining({ itemName: 'your item' })
      );
    });

    it('should handle errors gracefully', async () => {
      service.templateManager.renderTemplate.mockRejectedValueOnce(new Error('Template error'));

      const result = await service.sendPayoutReceivedEmail(owner, rental);

      expect(result.success).toBe(false);
      expect(result.error).toBe('Template error');
    });
  });

  describe('sendAuthenticationRequiredEmail', () => {
    const email = 'jane@example.com';
    const data = {
      renterName: 'Jane',
      itemName: 'Power Drill',
      ownerName: 'John',
      amount: 50,
    };

    it('should send authentication required email with correct variables', async () => {
      const result = await service.sendAuthenticationRequiredEmail(email, data);

      expect(result.success).toBe(true);
      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'authenticationRequiredToRenter',
        expect.objectContaining({
          renterName: 'Jane',
          itemName: 'Power Drill',
          ownerName: 'John',
          amount: '50.00',
        })
      );
      expect(service.emailClient.sendEmail).toHaveBeenCalledWith(
        'jane@example.com',
        'Action Required: Complete payment for Power Drill',
        expect.any(String)
      );
    });

    it('should use default values when data is missing', async () => {
      const minimalData = {};

      await service.sendAuthenticationRequiredEmail(email, minimalData);

      expect(service.templateManager.renderTemplate).toHaveBeenCalledWith(
        'authenticationRequiredToRenter',
        expect.objectContaining({
          renterName: 'there',
          itemName: 'the item',
          ownerName: 'The owner',
          amount: '0.00',
        })
      );
    });

    it('should handle errors gracefully', async () => {
      service.emailClient.sendEmail.mockRejectedValueOnce(new Error('Send error'));

      const result = await service.sendAuthenticationRequiredEmail(email, data);

      expect(result.success).toBe(false);
      expect(result.error).toBe('Send error');
    });
  });
});
