const request = require('supertest');

jest.mock('../src/lib/prisma', () => require('./__mocks__/prisma'));
jest.mock('../src/lib/cache', () => ({ get: jest.fn(() => undefined), set: jest.fn(), del: jest.fn(), delByPrefix: jest.fn() }));

const prisma = require('../src/lib/prisma');
const app = require('../src/app');

const EXP_ID = 'aaaaaaaa-0000-0000-0000-000000000001';

const EXPERIMENT = {
  id: EXP_ID,
  title: 'Calendar Test Study',
  created_at: new Date('2024-01-01').toISOString(),
};

// Helper: build a DailyStatus record
function makeStatus({ date, vitals = null, customFields = null }) {
  return {
    date: new Date(date),
    experiment_description: null,
    vitals,
    treatment: null,
    notes: null,
    custom_fields: customFields,
  };
}

beforeEach(() => {
  jest.clearAllMocks();
});

describe('GET /api/experiments/:id/calendar', () => {
  it('returns 404 when experiment does not exist', async () => {
    prisma.experiment.findUnique.mockResolvedValue(null);
    const res = await request(app).get(`/api/experiments/${EXP_ID}/calendar?field=vitals`);
    expect(res.status).toBe(404);
    expect(res.body.error).toMatch(/not found/i);
  });

  it('returns empty days object when no statuses exist', async () => {
    prisma.experiment.findUnique.mockResolvedValue(EXPERIMENT);
    prisma.dailyStatus.findMany.mockResolvedValue([]);
    const res = await request(app).get(`/api/experiments/${EXP_ID}/calendar?field=vitals`);
    expect(res.status).toBe(200);
    expect(res.body.days).toEqual({});
    expect(res.body.field).toBe('vitals');
  });

  it('counts subjects with non-empty builtin field per day', async () => {
    prisma.experiment.findUnique.mockResolvedValue(EXPERIMENT);
    prisma.dailyStatus.findMany.mockResolvedValue([
      makeStatus({ date: '2026-03-10', vitals: '120/80' }),
      makeStatus({ date: '2026-03-10', vitals: '110/70' }),
      makeStatus({ date: '2026-03-11', vitals: '115/75' }),
      makeStatus({ date: '2026-03-12', vitals: null }),      // null — not counted
      makeStatus({ date: '2026-03-12', vitals: '' }),        // empty — not counted
    ]);

    const res = await request(app).get(`/api/experiments/${EXP_ID}/calendar?field=vitals`);
    expect(res.status).toBe(200);
    expect(res.body.days).toEqual({
      '2026-03-10': 2,
      '2026-03-11': 1,
    });
  });

  it('counts subjects with non-empty custom field per day', async () => {
    const FIELD_ID = 'cccccccc-0000-0000-0000-000000000099';
    prisma.experiment.findUnique.mockResolvedValue(EXPERIMENT);
    prisma.dailyStatus.findMany.mockResolvedValue([
      makeStatus({ date: '2026-04-01', customFields: { [FIELD_ID]: '320' } }),
      makeStatus({ date: '2026-04-01', customFields: { [FIELD_ID]: '310' } }),
      makeStatus({ date: '2026-04-01', customFields: { [FIELD_ID]: null } }),  // null — not counted
      makeStatus({ date: '2026-04-02', customFields: { [FIELD_ID]: '315' } }),
    ]);

    const res = await request(app).get(`/api/experiments/${EXP_ID}/calendar?field=${FIELD_ID}`);
    expect(res.status).toBe(200);
    expect(res.body.days).toEqual({
      '2026-04-01': 2,
      '2026-04-02': 1,
    });
  });

  it('ignores whitespace-only custom field values', async () => {
    const FIELD_ID = 'dddddddd-0000-0000-0000-000000000099';
    prisma.experiment.findUnique.mockResolvedValue(EXPERIMENT);
    prisma.dailyStatus.findMany.mockResolvedValue([
      makeStatus({ date: '2026-04-05', customFields: { [FIELD_ID]: '   ' } }),
      makeStatus({ date: '2026-04-05', customFields: { [FIELD_ID]: '280' } }),
    ]);

    const res = await request(app).get(`/api/experiments/${EXP_ID}/calendar?field=${FIELD_ID}`);
    expect(res.status).toBe(200);
    expect(res.body.days).toEqual({ '2026-04-05': 1 });
  });

  it('returns days from multiple months correctly', async () => {
    prisma.experiment.findUnique.mockResolvedValue(EXPERIMENT);
    prisma.dailyStatus.findMany.mockResolvedValue([
      makeStatus({ date: '2026-02-15', vitals: '100/60' }),
      makeStatus({ date: '2026-03-01', vitals: '105/65' }),
      makeStatus({ date: '2026-03-01', vitals: '108/68' }),
    ]);

    const res = await request(app).get(`/api/experiments/${EXP_ID}/calendar?field=vitals`);
    expect(res.status).toBe(200);
    expect(res.body.days['2026-02-15']).toBe(1);
    expect(res.body.days['2026-03-01']).toBe(2);
  });

  it('returns field key in response body', async () => {
    prisma.experiment.findUnique.mockResolvedValue(EXPERIMENT);
    prisma.dailyStatus.findMany.mockResolvedValue([]);
    const res = await request(app).get(`/api/experiments/${EXP_ID}/calendar?field=treatment`);
    expect(res.status).toBe(200);
    expect(res.body.field).toBe('treatment');
  });

  it('handles missing field param gracefully (empty days)', async () => {
    prisma.experiment.findUnique.mockResolvedValue(EXPERIMENT);
    prisma.dailyStatus.findMany.mockResolvedValue([
      makeStatus({ date: '2026-04-01', vitals: 'present' }),
    ]);
    // No ?field= → all values undefined, nothing counted
    const res = await request(app).get(`/api/experiments/${EXP_ID}/calendar`);
    expect(res.status).toBe(200);
    expect(res.body.days).toEqual({});
  });
});
