import { test, expect, fixture, api, dbRows, evidence } from './helpers';

test('stranded-clock approval/export guards and cross-store punch IDs fail closed', async ({ request }) => {
  const member = fixture.actors.manager.memberships.b;
  const week = '2026-04-06';
  const punch = await api(request, 'manager', 'POST', '/punches', { store_membership_id: member, punch_type: 'clock_in', punched_at: week + 'T16:00:00Z', note: fixture.marker + ' stranded guard' }, 201, 'b');
  await api(request, 'manager', 'PATCH', `/punches/${punch.id}`, { revision: punch.revision, note: 'cross-store' }, 404, 'a');
  await api(request, 'manager', 'POST', '/punches', { store_membership_id: fixture.actors.staff.memberships.a, punch_type: 'clock_in', punched_at: week + 'T16:00:00Z', note: 'cross-store member' }, 404, 'b');
  const sheets = await api(request, 'manager', 'POST', '/timesheets/recalculate', { week_start: week }, 200, 'b');
  const sheet = sheets.find((t: any) => t.store_membership_id === member);
  expect(sheet.calculation_metadata.stranded_clock_ins.length).toBeGreaterThan(0);
  await api(request, 'manager', 'POST', `/timesheets/${sheet.id}/approve`, { acknowledge_oversized_sessions: true }, 422, 'b');
  await api(request, 'owner', 'POST', '/payroll/exports', { week_starts: [week], acknowledge_oversized_sessions: true }, 422, 'b', fixture.marker + '-stranded');
  const unchanged = await api(request, 'manager', 'GET', `/timesheets/${sheet.id}`, undefined, 200, 'b');
  expect(unchanged.status).toBe('pending');
  expect(dbRows('payroll_exports', 'b')).toHaveLength(0);
  await evidence('stranded-guards', { sheet: unchanged, punches: dbRows('time_punches', 'b'), exports: dbRows('payroll_exports', 'b') });
  // Close the intentionally stranded fixture through the real API. Otherwise it
  // correctly blocks later weeks' exports for the whole store in another case.
  await api(request, 'manager', 'POST', '/punches', { store_membership_id: member, punch_type: 'clock_out', punched_at: week + 'T17:00:00Z', note: fixture.marker + ' bounded guard cleanup' }, 201, 'b');
  const closed = dbRows('time_punches', 'b').filter((p: any) => p.store_membership_id === member && !p.deleted_at);
  expect(closed.map((p: any) => p.punch_type).sort()).toEqual(['clock_in', 'clock_out']);
});
