import { afterEach, beforeEach, expect, test } from 'bun:test';
import { fixture } from './test-fixture';
let f: ReturnType<typeof fixture>;
beforeEach(() => { f = fixture(); });
afterEach(() => f.close());
async function seeded() {
  const habit = await f.json('/habits', 'POST', { name: 'Routine', method: 'tasks', color: '#397f76', tasks: [{ name: 'Stretch' }] }, 201);
  await f.json(`/habits/${habit.id}/days/2026-09-04/tasks/${habit.tasks[0].id}`, 'PUT', { done: true });
  const other = await f.json('/habits', 'POST', { name: 'Keep', method: 'manual' }, 201);
  const mixed = await f.json('/charts', 'POST', { name: 'Mixed', habitIds: [habit.id, other.id] }, 201);
  const solo = await f.json('/charts', 'POST', { name: 'Solo', habitIds: [habit.id] }, 201);
  await f.request(`/habits/${habit.id}`, 'DELETE');
  return { habit, other, mixed, solo };
}
test('permanent deletion removes all dependent records and repairs combined charts', async () => {
  const { habit, other, mixed, solo } = await seeded();
  const dayIds = f.sqlite.query('SELECT id FROM habit_days WHERE habit_id = ?').all(habit.id) as { id: number }[];
  expect(dayIds.length).toBeGreaterThan(0);
  expect(f.sqlite.query('SELECT count(*) AS n FROM progress_events').get()).toEqual({ n: 1 });
  expect((await f.request(`/habits/${habit.id}/permanent`, 'DELETE')).status).toBe(204);
  for (const table of ['habit_days', 'habit_revisions', 'habit_calendar_settings']) {
    expect(f.sqlite.query(`SELECT count(*) AS n FROM ${table} WHERE habit_id = ?`).get(habit.id)).toEqual({ n: 0 });
  }
  for (const day of dayIds) for (const table of ['progress_events', 'task_occurrences']) {
    expect(f.sqlite.query(`SELECT count(*) AS n FROM ${table} WHERE day_id = ?`).get(day.id)).toEqual({ n: 0 });
  }
  expect((await f.request(`/habits/${habit.id}/history`)).status).toBe(404);
  expect((await f.request(`/habits/${habit.id}`, 'PATCH', { archived: false })).status).toBe(404);
  expect((await f.json(`/charts/${mixed.id}`)).habitIds).toEqual([other.id]);
  expect((await f.request(`/charts/${solo.id}`)).status).toBe(404);
  expect((await f.json('/habits?archived=true')).habits.map((h: { id: string }) => h.id)).toEqual([other.id]);
});
test('deletion requires ownership, same origin, authentication, and an archived habit', async () => {
  const { habit, other } = await seeded();
  expect((await f.request(`/habits/${habit.id}/permanent`, 'DELETE', undefined, 'b')).status).toBe(404);
  expect((await f.request(`/habits/${habit.id}/permanent`, 'DELETE', undefined, 'x')).status).toBe(401);
  expect((await f.request(`/habits/${habit.id}/permanent`, 'DELETE', undefined, 'a', { Origin: 'https://elsewhere.example' })).status).toBe(403);
  expect((await f.request(`/habits/${other.id}/permanent`, 'DELETE')).status).toBe(409);
  expect((await f.json(`/habits/${habit.id}`)).archived).toBe(true);
});
test('a failed deletion rolls back history and chart changes', async () => {
  const { habit, mixed, solo } = await seeded();
  const before = f.sqlite.query('SELECT * FROM progress_events').all();
  f.sqlite.exec("CREATE TRIGGER reject_habit_delete BEFORE DELETE ON habits BEGIN SELECT RAISE(ABORT, 'test failure'); END");
  expect((await f.request(`/habits/${habit.id}/permanent`, 'DELETE')).status).toBe(500);
  expect(f.sqlite.query('SELECT * FROM progress_events').all()).toEqual(before);
  expect((await f.json(`/charts/${mixed.id}`)).habitIds).toContain(habit.id);
  expect((await f.request(`/charts/${solo.id}`)).status).toBe(200);
  expect((await f.json(`/habits/${habit.id}`)).archived).toBe(true);
});
