/**
 * lib/services/route53-sync-service.ts unit tests.
 *
 * Only buildDriftHistoryRows is exercised here — a pure function taking
 * hand-constructed NormalizedRecordSet maps, no AWS or Postgres mocking
 * required (matches the plan's stated test scope for this file).
 */

import { describe, it, expect } from 'vitest';
import { buildDriftHistoryRows } from './route53-sync-service';
import type { NormalizedRecordSet } from './route53-record-key';

function makeRecordSet(overrides: Partial<NormalizedRecordSet> = {}): NormalizedRecordSet {
  return {
    recordKey: 'Z123:www.example.com.:A:',
    zoneId: 'Z123',
    name: 'www.example.com.',
    type: 'A',
    setIdentifier: null,
    ttl: 300,
    resourceRecords: [{ value: '10.0.0.1' }],
    aliasTarget: null,
    ...overrides,
  };
}

describe('buildDriftHistoryRows', () => {
  it('produces one update row with non-null before/after when TTL changes', () => {
    const key = 'Z123:www.example.com.:A:';
    const prev = makeRecordSet({ ttl: 300 });
    const next = makeRecordSet({ ttl: 600 });

    const rows = buildDriftHistoryRows(
      new Map([[key, prev]]),
      new Map([[key, next]]),
      'Z123'
    );

    expect(rows).toHaveLength(1);
    expect(rows[0].changeAction).toBe('update');
    expect(rows[0].zoneId).toBe('Z123');
    expect(rows[0].recordKey).toBe(key);
    expect(rows[0].beforeValue).not.toBeNull();
    expect(rows[0].afterValue).not.toBeNull();
  });

  it('produces a delete row with non-null before and null after when the mirror row has no AWS match', () => {
    const key = 'Z123:gone.example.com.:A:';
    const prev = makeRecordSet({ recordKey: key, name: 'gone.example.com.' });

    const rows = buildDriftHistoryRows(new Map([[key, prev]]), new Map(), 'Z123');

    expect(rows).toHaveLength(1);
    expect(rows[0].changeAction).toBe('delete');
    expect(rows[0].beforeValue).not.toBeNull();
    expect(rows[0].afterValue).toBeNull();
  });

  it('produces a create row with null before and non-null after when AWS has no matching mirror row', () => {
    const key = 'Z123:new.example.com.:A:';
    const next = makeRecordSet({ recordKey: key, name: 'new.example.com.' });

    const rows = buildDriftHistoryRows(new Map(), new Map([[key, next]]), 'Z123');

    expect(rows).toHaveLength(1);
    expect(rows[0].changeAction).toBe('create');
    expect(rows[0].beforeValue).toBeNull();
    expect(rows[0].afterValue).not.toBeNull();
  });

  it('produces zero rows when mirror and AWS recordsets are identical', () => {
    const key = 'Z123:www.example.com.:A:';
    const prev = makeRecordSet();
    const next = makeRecordSet();

    const rows = buildDriftHistoryRows(
      new Map([[key, prev]]),
      new Map([[key, next]]),
      'Z123'
    );

    expect(rows).toHaveLength(0);
  });

  it('stores the whole recordset (name, type, setIdentifier, ttl, resourceRecords, aliasTarget) in before/after, not a per-field delta', () => {
    const key = 'Z123:www.example.com.:A:';
    const prev = makeRecordSet({ ttl: 300 });
    const next = makeRecordSet({ ttl: 600 });

    const rows = buildDriftHistoryRows(
      new Map([[key, prev]]),
      new Map([[key, next]]),
      'Z123'
    );

    expect(rows[0].afterValue).toEqual({
      name: 'www.example.com.',
      type: 'A',
      setIdentifier: null,
      ttl: 600,
      resourceRecords: [{ value: '10.0.0.1' }],
      aliasTarget: null,
    });
  });

  it('a record changed by Pulse CRUD in the same window is still tagged as drift by the sync (sync cannot distinguish actor)', () => {
    // The sync has no notion of "who changed this" — it only compares
    // mirror vs. AWS-live state. A CRUD-originated change looks identical
    // to an out-of-band AWS console edit from the sync's perspective, so it
    // still produces a drift row here. The CRUD route's own pulse_crud row
    // (written elsewhere, not by this function) is the authoritative one;
    // both rows are expected to coexist in the ledger.
    const key = 'Z123:www.example.com.:A:';
    const prev = makeRecordSet({ ttl: 300 });
    const next = makeRecordSet({ ttl: 900 }); // as if Pulse CRUD had just updated this

    const rows = buildDriftHistoryRows(
      new Map([[key, prev]]),
      new Map([[key, next]]),
      'Z123'
    );

    expect(rows).toHaveLength(1);
    expect(rows[0].changeAction).toBe('update');
  });
});
