const request = require('supertest');

jest.mock('../src/lib/prisma', () => require('./__mocks__/prisma'));
const prisma = require('../src/lib/prisma');
const app = require('../src/app');

const ANIMAL_ID = 'dddddddd-0000-0000-0000-000000000001';
const STATUS = {
  id: 'eeeeeeee-0000-0000-0000-000000000001',
  animal_id: ANIMAL_ID,
  date: new Date('2024-06-01').toISOString(),
  experiment_description: 'Baseline measurement',
  vitals: 'HR 72, Temp 37.2',
  treatment: 'Control',
  notes: 'Healthy',
};

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

describe('GET /api/daily-statuses', () => {
  it('returns statuses filtered by animalId', async () => {
    prisma.dailyStatus.findMany.mockResolvedValue([STATUS]);
    const res = await request(app).get(`/api/daily-statuses?animalId=${ANIMAL_ID}`);
    expect(res.status).toBe(200);
    expect(res.body).toHaveLength(1);
    expect(res.body[0].vitals).toBe('HR 72, Temp 37.2');
  });

  it('returns 422 for invalid animalId UUID', async () => {
    const res = await request(app).get('/api/daily-statuses?animalId=invalid');
    expect(res.status).toBe(422);
  });
});

describe('GET /api/daily-statuses/:id', () => {
  it('returns a single status entry', async () => {
    prisma.dailyStatus.findUnique.mockResolvedValue({ ...STATUS, animal: {} });
    const res = await request(app).get(`/api/daily-statuses/${STATUS.id}`);
    expect(res.status).toBe(200);
    expect(res.body.id).toBe(STATUS.id);
  });

  it('returns 404 for unknown id', async () => {
    prisma.dailyStatus.findUnique.mockResolvedValue(null);
    const res = await request(app).get('/api/daily-statuses/00000000-0000-0000-0000-000000000000');
    expect(res.status).toBe(404);
  });
});

describe('POST /api/daily-statuses', () => {
  it('creates status and returns 201 with UUID', async () => {
    prisma.dailyStatus.create.mockResolvedValue(STATUS);
    prisma.auditLog.create.mockResolvedValue({});

    const res = await request(app)
      .post('/api/daily-statuses')
      .send({ animal_id: ANIMAL_ID, date: '2024-06-01', vitals: 'HR 72' });

    expect(res.status).toBe(201);
    expect(res.body.id).toMatch(/^[0-9a-f-]{36}$/i);
    expect(prisma.auditLog.create).toHaveBeenCalled();
  });

  it('returns 422 when animal_id is missing', async () => {
    const res = await request(app)
      .post('/api/daily-statuses')
      .send({ date: '2024-06-01' });
    expect(res.status).toBe(422);
    expect(res.body.details).toEqual(
      expect.arrayContaining([expect.objectContaining({ field: 'animal_id' })])
    );
  });

  it('returns 422 when date is missing', async () => {
    const res = await request(app)
      .post('/api/daily-statuses')
      .send({ animal_id: ANIMAL_ID });
    expect(res.status).toBe(422);
    expect(res.body.details).toEqual(
      expect.arrayContaining([expect.objectContaining({ field: 'date' })])
    );
  });

  it('returns 422 when date is not a valid ISO date', async () => {
    const res = await request(app)
      .post('/api/daily-statuses')
      .send({ animal_id: ANIMAL_ID, date: 'not-a-date' });
    expect(res.status).toBe(422);
  });
});

describe('PUT /api/daily-statuses/:id', () => {
  it('updates status and writes UPDATE audit log', async () => {
    const updated = { ...STATUS, vitals: 'HR 80' };
    prisma.dailyStatus.findUnique
      .mockResolvedValueOnce(STATUS)
      .mockResolvedValueOnce(updated);
    prisma.dailyStatus.update.mockResolvedValue(updated);
    prisma.auditLog.create.mockResolvedValue({});

    const res = await request(app)
      .put(`/api/daily-statuses/${STATUS.id}`)
      .send({ vitals: 'HR 80' });

    expect(res.status).toBe(200);
    expect(res.body.vitals).toBe('HR 80');
    expect(prisma.auditLog.create).toHaveBeenCalledWith(
      expect.objectContaining({ data: expect.objectContaining({ action: 'UPDATE' }) })
    );
  });

  it('returns 422 when date format is invalid', async () => {
    prisma.dailyStatus.findUnique.mockResolvedValue(STATUS);
    const res = await request(app)
      .put(`/api/daily-statuses/${STATUS.id}`)
      .send({ date: 'garbage' });
    expect(res.status).toBe(422);
  });
});

describe('DELETE /api/daily-statuses/:id', () => {
  it('deletes status and writes DELETE audit log', async () => {
    prisma.dailyStatus.findUnique.mockResolvedValue(STATUS);
    prisma.dailyStatus.delete.mockResolvedValue(STATUS);
    prisma.auditLog.create.mockResolvedValue({});

    const res = await request(app).delete(`/api/daily-statuses/${STATUS.id}`);
    expect(res.status).toBe(204);
    expect(prisma.auditLog.create).toHaveBeenCalledWith(
      expect.objectContaining({ data: expect.objectContaining({ action: 'DELETE' }) })
    );
  });
});
