const request = require('supertest');
const { app } = require('../../backend/server');
const { cleanDb, getAuthHeader, seedSession } = require('../helpers/test-utils');

describe('GET /api/sessions — filter and limit', () => {
  beforeEach(() => {
    cleanDb();
  });

  test('default filter excludes archived sessions', async () => {
    seedSession({ is_active: 0, notes: null });
    const archived = seedSession({ is_active: 0, notes: null });
    require('../helpers/test-utils').db.prepare(
      'UPDATE sessions SET archived = 1 WHERE id = ?'
    ).run(archived.id);

    const res = await request(app).get('/api/sessions');
    expect(res.status).toBe(200);
    expect(res.body).toHaveLength(1);
    expect(res.body[0].archived).toBe(0);
  });

  test('filter=archived returns only archived sessions', async () => {
    seedSession({ is_active: 0, notes: null });
    const archived = seedSession({ is_active: 0, notes: null });
    require('../helpers/test-utils').db.prepare(
      'UPDATE sessions SET archived = 1 WHERE id = ?'
    ).run(archived.id);

    const res = await request(app).get('/api/sessions?filter=archived');
    expect(res.status).toBe(200);
    expect(res.body).toHaveLength(1);
    expect(res.body[0].archived).toBe(1);
  });

  test('filter=all returns all sessions', async () => {
    seedSession({ is_active: 0, notes: null });
    const archived = seedSession({ is_active: 0, notes: null });
    require('../helpers/test-utils').db.prepare(
      'UPDATE sessions SET archived = 1 WHERE id = ?'
    ).run(archived.id);

    const res = await request(app).get('/api/sessions?filter=all');
    expect(res.status).toBe(200);
    expect(res.body).toHaveLength(2);
  });

  test('limit restricts number of sessions returned', async () => {
    for (let i = 0; i < 10; i++) {
      seedSession({ is_active: 0, notes: null });
    }

    const res = await request(app).get('/api/sessions?filter=all&limit=3');
    expect(res.status).toBe(200);
    expect(res.body).toHaveLength(3);
  });

  test('limit=all returns all sessions', async () => {
    for (let i = 0; i < 10; i++) {
      seedSession({ is_active: 0, notes: null });
    }

    const res = await request(app).get('/api/sessions?filter=all&limit=all');
    expect(res.status).toBe(200);
    expect(res.body).toHaveLength(10);
  });

  test('X-Total-Count header reflects total matching sessions before limit', async () => {
    for (let i = 0; i < 10; i++) {
      seedSession({ is_active: 0, notes: null });
    }

    const res = await request(app).get('/api/sessions?filter=all&limit=3');
    expect(res.headers['x-total-count']).toBe('10');
    expect(res.body).toHaveLength(3);
  });

  test('response includes archived field on each session', async () => {
    seedSession({ is_active: 0, notes: null });

    const res = await request(app).get('/api/sessions?filter=all');
    expect(res.status).toBe(200);
    expect(res.body[0]).toHaveProperty('archived', 0);
  });

  test('default limit is all when no limit param provided', async () => {
    for (let i = 0; i < 8; i++) {
      seedSession({ is_active: 0, notes: null });
    }

    const res = await request(app).get('/api/sessions?filter=all');
    expect(res.status).toBe(200);
    expect(res.body).toHaveLength(8);
  });

  test('offset skips the first N sessions', async () => {
    for (let i = 0; i < 5; i++) {
      seedSession({ is_active: 0, notes: null });
    }

    const allRes = await request(app).get('/api/sessions?filter=all&limit=all');
    const offsetRes = await request(app).get('/api/sessions?filter=all&limit=2&offset=2');
    expect(offsetRes.status).toBe(200);
    expect(offsetRes.body).toHaveLength(2);
    expect(offsetRes.body[0].id).toBe(allRes.body[2].id);
    expect(offsetRes.body[1].id).toBe(allRes.body[3].id);
  });

  test('offset defaults to 0 when not provided', async () => {
    for (let i = 0; i < 3; i++) {
      seedSession({ is_active: 0, notes: null });
    }

    const res = await request(app).get('/api/sessions?filter=all&limit=2');
    expect(res.status).toBe(200);
    expect(res.body).toHaveLength(2);
  });

  test('negative offset is clamped to 0', async () => {
    seedSession({ is_active: 0, notes: null });

    const res = await request(app).get('/api/sessions?filter=all&offset=-5');
    expect(res.status).toBe(200);
    expect(res.body).toHaveLength(1);
  });

  test('non-numeric offset is clamped to 0', async () => {
    seedSession({ is_active: 0, notes: null });

    const res = await request(app).get('/api/sessions?filter=all&offset=abc');
    expect(res.status).toBe(200);
    expect(res.body).toHaveLength(1);
  });

  test('offset past end returns empty array', async () => {
    seedSession({ is_active: 0, notes: null });

    const res = await request(app).get('/api/sessions?filter=all&limit=5&offset=100');
    expect(res.status).toBe(200);
    expect(res.body).toHaveLength(0);
    expect(res.headers['x-total-count']).toBe('1');
  });

  test('X-Prev-Last-Date header is set with correct value when offset > 0', async () => {
    for (let i = 0; i < 5; i++) {
      seedSession({ is_active: 0, notes: null });
    }

    const allRes = await request(app).get('/api/sessions?filter=all&limit=all');
    const res = await request(app).get('/api/sessions?filter=all&limit=2&offset=2');
    expect(res.headers['x-prev-last-date']).toBe(allRes.body[1].created_at);
  });

  test('X-Prev-Last-Date header is absent when offset is 0', async () => {
    seedSession({ is_active: 0, notes: null });

    const res = await request(app).get('/api/sessions?filter=all&limit=2');
    expect(res.headers['x-prev-last-date']).toBeUndefined();
  });

  test('X-Total-Count is unaffected by offset', async () => {
    for (let i = 0; i < 10; i++) {
      seedSession({ is_active: 0, notes: null });
    }

    const res = await request(app).get('/api/sessions?filter=all&limit=3&offset=6');
    expect(res.headers['x-total-count']).toBe('10');
    expect(res.body).toHaveLength(3);
  });

  test('offset works with filter=default', async () => {
    for (let i = 0; i < 5; i++) {
      seedSession({ is_active: 0, notes: null });
    }
    const archived = seedSession({ is_active: 0, notes: null });
    require('../helpers/test-utils').db.prepare(
      'UPDATE sessions SET archived = 1 WHERE id = ?'
    ).run(archived.id);

    const res = await request(app).get('/api/sessions?filter=default&limit=2&offset=2');
    expect(res.status).toBe(200);
    expect(res.body).toHaveLength(2);
    expect(res.headers['x-total-count']).toBe('5');
    res.body.forEach(s => expect(s.archived).toBe(0));
  });
});

describe('POST /api/sessions/:id/archive', () => {
  beforeEach(() => {
    cleanDb();
  });

  test('archives a closed session', async () => {
    const session = seedSession({ is_active: 0, notes: null });

    const res = await request(app)
      .post(`/api/sessions/${session.id}/archive`)
      .set('Authorization', getAuthHeader());

    expect(res.status).toBe(200);
    expect(res.body.success).toBe(true);

    const check = await request(app).get(`/api/sessions/${session.id}`);
    expect(check.body.archived).toBe(1);
  });

  test('returns 400 for active session', async () => {
    const session = seedSession({ is_active: 1, notes: null });

    const res = await request(app)
      .post(`/api/sessions/${session.id}/archive`)
      .set('Authorization', getAuthHeader());

    expect(res.status).toBe(400);
  });

  test('returns 404 for non-existent session', async () => {
    const res = await request(app)
      .post('/api/sessions/9999/archive')
      .set('Authorization', getAuthHeader());

    expect(res.status).toBe(404);
  });

  test('returns 401 without auth', async () => {
    const session = seedSession({ is_active: 0, notes: null });

    const res = await request(app)
      .post(`/api/sessions/${session.id}/archive`);

    expect(res.status).toBe(401);
  });
});

describe('POST /api/sessions/:id/unarchive', () => {
  beforeEach(() => {
    cleanDb();
  });

  test('unarchives an archived session', async () => {
    const session = seedSession({ is_active: 0, notes: null });
    require('../helpers/test-utils').db.prepare(
      'UPDATE sessions SET archived = 1 WHERE id = ?'
    ).run(session.id);

    const res = await request(app)
      .post(`/api/sessions/${session.id}/unarchive`)
      .set('Authorization', getAuthHeader());

    expect(res.status).toBe(200);
    expect(res.body.success).toBe(true);

    const check = await request(app).get(`/api/sessions/${session.id}`);
    expect(check.body.archived).toBe(0);
  });

  test('returns 404 for non-existent session', async () => {
    const res = await request(app)
      .post('/api/sessions/9999/unarchive')
      .set('Authorization', getAuthHeader());

    expect(res.status).toBe(404);
  });
});

describe('POST /api/sessions/bulk', () => {
  beforeEach(() => {
    cleanDb();
  });

  test('bulk archive multiple sessions', async () => {
    const s1 = seedSession({ is_active: 0, notes: null });
    const s2 = seedSession({ is_active: 0, notes: null });

    const res = await request(app)
      .post('/api/sessions/bulk')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({ action: 'archive', ids: [s1.id, s2.id] });

    expect(res.status).toBe(200);
    expect(res.body.success).toBe(true);
    expect(res.body.affected).toBe(2);

    const list = await request(app).get('/api/sessions?filter=archived');
    expect(list.body).toHaveLength(2);
  });

  test('bulk unarchive multiple sessions', async () => {
    const s1 = seedSession({ is_active: 0, notes: null });
    const s2 = seedSession({ is_active: 0, notes: null });
    const db = require('../helpers/test-utils').db;
    db.prepare('UPDATE sessions SET archived = 1 WHERE id IN (?, ?)').run(s1.id, s2.id);

    const res = await request(app)
      .post('/api/sessions/bulk')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({ action: 'unarchive', ids: [s1.id, s2.id] });

    expect(res.status).toBe(200);
    expect(res.body.affected).toBe(2);

    const list = await request(app).get('/api/sessions?filter=all');
    expect(list.body.every(s => s.archived === 0)).toBe(true);
  });

  test('bulk delete multiple sessions', async () => {
    const s1 = seedSession({ is_active: 0, notes: null });
    const s2 = seedSession({ is_active: 0, notes: null });

    const res = await request(app)
      .post('/api/sessions/bulk')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({ action: 'delete', ids: [s1.id, s2.id] });

    expect(res.status).toBe(200);
    expect(res.body.affected).toBe(2);

    const list = await request(app).get('/api/sessions?filter=all');
    expect(list.body).toHaveLength(0);
  });

  test('rejects archive of active sessions', async () => {
    const active = seedSession({ is_active: 1, notes: null });
    const closed = seedSession({ is_active: 0, notes: null });

    const res = await request(app)
      .post('/api/sessions/bulk')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({ action: 'archive', ids: [active.id, closed.id] });

    expect(res.status).toBe(400);
    expect(res.body.activeIds).toContain(active.id);

    const list = await request(app).get('/api/sessions?filter=all');
    expect(list.body).toHaveLength(2);
    expect(list.body.every(s => s.archived === 0)).toBe(true);
  });

  test('rejects delete of active sessions', async () => {
    const active = seedSession({ is_active: 1, notes: null });

    const res = await request(app)
      .post('/api/sessions/bulk')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({ action: 'delete', ids: [active.id] });

    expect(res.status).toBe(400);
  });

  test('returns 400 for empty ids array', async () => {
    const res = await request(app)
      .post('/api/sessions/bulk')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({ action: 'archive', ids: [] });

    expect(res.status).toBe(400);
  });

  test('returns 400 for invalid action', async () => {
    const res = await request(app)
      .post('/api/sessions/bulk')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({ action: 'nuke', ids: [1] });

    expect(res.status).toBe(400);
  });

  test('returns 400 for non-array ids', async () => {
    const res = await request(app)
      .post('/api/sessions/bulk')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({ action: 'archive', ids: 'not-array' });

    expect(res.status).toBe(400);
  });

  test('returns 404 if any session ID does not exist', async () => {
    const s1 = seedSession({ is_active: 0, notes: null });

    const res = await request(app)
      .post('/api/sessions/bulk')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({ action: 'archive', ids: [s1.id, 9999] });

    expect(res.status).toBe(404);
  });

  test('returns 401 without auth', async () => {
    const res = await request(app)
      .post('/api/sessions/bulk')
      .set('Content-Type', 'application/json')
      .send({ action: 'archive', ids: [1] });

    expect(res.status).toBe(401);
  });
});
