/**
 * Unit tests for the pure helpers of the backup-destinations service:
 * schedule-due logic + secret config (serialize → encrypt at rest →
 * decrypt for use, mask for API). DB-backed CRUD is covered by the e2e
 * verification, not here.
 */

import { describe, expect, it } from 'vitest';

import {
  decryptConfig,
  isScheduleDue,
  maskConfig,
  serializeConfig,
} from '@/lib/services/backup-destinations.service';

describe('isScheduleDue', () => {
  // 2026-06-07 is a Sunday; 2026-06-08 a Monday.
  const sunday = new Date('2026-06-07T02:00:00Z');
  const monday = new Date('2026-06-08T02:00:00Z');

  it('off is never due', () => {
    expect(isScheduleDue('off', sunday)).toBe(false);
    expect(isScheduleDue('off', monday)).toBe(false);
  });
  it('daily is always due', () => {
    expect(isScheduleDue('daily', sunday)).toBe(true);
    expect(isScheduleDue('daily', monday)).toBe(true);
  });
  it('weekly is due only on Sunday', () => {
    expect(isScheduleDue('weekly', sunday)).toBe(true);
    expect(isScheduleDue('weekly', monday)).toBe(false);
  });
});

describe('secret config handling', () => {
  it('serialize encrypts secrets, decrypt restores them, mask hides them', () => {
    const incoming = {
      host: 'box.example.com',
      username: 'crm',
      password: 'hunter2',
      remoteDir: '/backups',
    };
    const stored = serializeConfig('sftp', incoming);

    // Stored password must not be the plaintext.
    expect(stored.password).not.toBe('hunter2');
    expect(stored.host).toBe('box.example.com');

    // Decrypt restores the plaintext for transport use.
    expect(decryptConfig('sftp', stored)).toMatchObject({
      host: 'box.example.com',
      username: 'crm',
      password: 'hunter2',
      remoteDir: '/backups',
    });

    // Mask hides the secret and exposes only a *IsSet marker.
    const masked = maskConfig('sftp', stored);
    expect(masked.password).toBeUndefined();
    expect(masked.passwordIsSet).toBe(true);
    expect(masked.host).toBe('box.example.com');
  });

  it('update with a blank secret keeps the existing encrypted value', () => {
    const original = serializeConfig('s3', {
      endpoint: 'https://s3.example.com',
      bucket: 'b',
      accessKey: 'AK',
      secretKey: 'SUPERSECRET',
    });
    // Admin edits the bucket but leaves the secret key blank (unchanged).
    const updated = serializeConfig(
      's3',
      { endpoint: 'https://s3.example.com', bucket: 'b2', accessKey: 'AK', secretKey: '' },
      original,
    );
    expect(updated.bucket).toBe('b2');
    expect(decryptConfig('s3', updated).secretKey).toBe('SUPERSECRET');
  });

  it('filesystem has no secrets to mask', () => {
    const stored = serializeConfig('filesystem', { directory: '/mnt/nas/backups' });
    expect(maskConfig('filesystem', stored)).toEqual({ directory: '/mnt/nas/backups' });
  });
});
