import { test, expect, fixture, api, dbRows, evidence, API, storePath } from './helpers';
import { createHash } from 'node:crypto';

test('real API payroll snapshot replays unchanged after settings mutation and rejects exported corrections', async ({ request }) => {
  const member = fixture.actors.owner.memberships.b, week = '2026-06-01';
  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 + ' export API setup' }, 201, 'b');
  await api(request, 'manager', 'POST', '/punches', { store_membership_id: member, punch_type: 'clock_out', punched_at: week + 'T17:00:00Z', note: fixture.marker + ' export API setup' }, 201, 'b');
  const corrected = await api(request, 'manager', 'PATCH', `/punches/${punch.id}`, { revision: punch.revision, punched_at: week + 'T15:00:00Z', note: fixture.marker + ' correct first hour' }, 200, 'b');
  await api(request, 'manager', 'PATCH', `/punches/${punch.id}`, { revision: punch.revision, note: 'stale' }, 409, '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.regular_minutes).toBe(120);
  expect(sheet).not.toHaveProperty('total_pay_cents');
  await api(request, 'manager', 'POST', `/timesheets/${sheet.id}/approve`, {}, 200, 'b');
  const approved = await api(request, 'owner', 'GET', `/timesheets/${sheet.id}`, undefined, 200, 'b');
  expect(approved.total_pay_cents).toBe(4800);
  const key = fixture.marker + '-snapshot', body = { week_starts: [week], acknowledge_oversized_sessions: false };
  const exported = await api(request, 'owner', 'POST', '/payroll/exports', body, 201, 'b', key);
  const download = async () => {
    const response = await request.get(API + storePath(`/payroll/exports/${exported.id}/download`, 'b'), { headers: { Authorization: ['Bearer', fixture.actors.owner.token].join(' ') } });
    expect(response.status()).toBe(200);
    return response.body();
  };
  const original = await download();
  expect(createHash('sha256').update(original).digest('hex')).toBe(exported.csv_hash);
  expect(original.toString()).toContain(fixture.actors.owner.name);
  await api(request, 'owner', 'PATCH', '/schedule/settings', { payroll_rounding_mode: 'up', payroll_rounding_increment_minutes: 15 }, 200, 'b');
  const replay = await api(request, 'owner', 'POST', '/payroll/exports', body, 200, 'b', key);
  expect(replay.id).toBe(exported.id);
  expect(await download()).toEqual(original);
  await api(request, 'owner', 'POST', '/payroll/exports', { ...body, week_starts: ['2026-06-08'] }, 409, 'b', key);
  await api(request, 'manager', 'POST', '/payroll/exports', body, 403, 'b', key);
  await api(request, 'manager', 'POST', `/timesheets/${sheet.id}/unlock`, {}, 409, 'b');
  await api(request, 'manager', 'PATCH', `/punches/${punch.id}`, { revision: corrected.revision, punched_at: week + 'T14:00:00Z', note: 'exported must remain immutable' }, 409, 'b');
  const exports = dbRows('payroll_exports', 'b');
  expect(exports).toHaveLength(1);
  expect(dbRows('time_punches', 'b').find((p: any) => p.id === punch.id).punched_at).toContain('15:00:00');
  await evidence('api-payroll-snapshot-replay', { approved, exported, replay, csv_sha256: exported.csv_hash, csv_bytes: original.length, exports });
});
