import { beforeEach, describe, expect, it, vi } from 'vitest';

const { requireAuthMock, getAbsenceDetailsMock, updateAbsenceFeedMock, deleteAbsenceFeedMock } = vi.hoisted(() => ({
  requireAuthMock: vi.fn(),
  getAbsenceDetailsMock: vi.fn(),
  updateAbsenceFeedMock: vi.fn(),
  deleteAbsenceFeedMock: vi.fn(),
}));

vi.mock('../../../../src/lib/requireAuth', () => ({
  requireAuth: requireAuthMock,
}));

vi.mock('../../../../src/server/absences.server', () => ({
  getAbsenceDetails: getAbsenceDetailsMock,
  updateAbsenceFeed: updateAbsenceFeedMock,
  deleteAbsenceFeed: deleteAbsenceFeedMock,
}));

import { GET, PUT, DELETE } from '../../../../src/app/api/absences/[id]/route';

const makeCtx = <T extends (...args: any[]) => any>(fn: T, params: Record<string, string>) => ({ params: Promise.resolve(params) } as unknown as Parameters<T>[1]);

describe('Unit — /api/absences/[id] route', () => {
  beforeEach(() => {
    vi.resetAllMocks();
    requireAuthMock.mockReturnValue(null);
  });

  it('GET returns 200 when absence exists', async () => {
    getAbsenceDetailsMock.mockResolvedValue({ absence: { TCS: 'Alice', id_absence: 123, id_salarie: 955 } });

    const res = await GET(new Request('http://localhost/api/absences/123'), makeCtx(GET, { id: '123' }));

    expect(res.status).toBe(200);
    const body = await res.json();
    expect(body).toEqual({ message: 'Absence retrieved successfully', absence: { TCS: 'Alice', id_absence: 123, id_salarie: 955 } });
  });

  it('GET returns 400 when id param is invalid', async () => {
    const res = await GET(new Request('http://localhost/api/absences/abc'), makeCtx(GET, { id: 'abc' }));

    expect(res.status).toBe(400);
    const body = await res.json();
    expect(body.message).toBe('Invalid ID parameter');
  });
  it('GET returns auth error when request is unauthorized', async () => {
    // simulate requireAuth returning an HTTP 401 Response
    requireAuthMock.mockReturnValue(new Response('Unauthorized', { status: 401 }));

    const res = await GET(new Request('http://localhost/api/absences/1'), makeCtx(GET, { id: '1' }));

    expect(res.status).toBe(401);
    expect(getAbsenceDetailsMock).not.toHaveBeenCalled();
    });
  it('GET returns 404 when not found', async () => {
    getAbsenceDetailsMock.mockResolvedValue({ absence: null });

    const res = await GET(new Request('http://localhost/api/absences/999'), makeCtx(GET, { id: '999' }));

    expect(res.status).toBe(404);
    const body = await res.json();
    expect(body.message).toBe('Absence not found');
  });

  it('PUT updates and returns 200', async () => {
    updateAbsenceFeedMock.mockResolvedValue({ absence: { TCS: 'Bob', id_absence: 123, id_salarie: 955 } });

    const res = await PUT(
      new Request('http://localhost/api/absences/123', { method: 'PUT', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ TCS: 'Bob' }) }),
      makeCtx(PUT, { id: '123' }),
    );

    expect(res.status).toBe(200);
    const body = await res.json();
    expect(body).toEqual({ message: 'Absence updated successfully', absence: { TCS: 'Bob', id_absence: 123, id_salarie: 955 } });
  });
  it('PUT returns 400 when id param is invalid', async () => {
    const res = await PUT(new Request('http://localhost/api/absences/abc'), makeCtx(PUT, { id: 'abc' }));

    expect(res.status).toBe(400);
    const body = await res.json();
    expect(body.message).toBe('Invalid ID parameter');
  });
  it('PUT returns 400 for invalid JSON', async () => {
    const res = await PUT(
      new Request('http://localhost/api/absences/123', { method: 'PUT', headers: { 'content-type': 'application/json' }, body: '{' }),
      makeCtx(PUT, { id: '123' }),
    );

    expect(res.status).toBe(400);
    const body = await res.json();
    expect(body.message).toBe('Invalid JSON body');
  });

  it('PUT returns 400 when validation fails', async () => {
    const res = await PUT(
      new Request('http://localhost/api/absences/123', { method: 'PUT', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id_salarie: 'not-a-number' }) }),
      makeCtx(PUT, { id: '123' }),
    );

    expect(res.status).toBe(400);
    const body = await res.json();
    expect(body.message).toBe('Invalid body parameters');
  });
  it('PUT returns auth error when request is unauthorized', async () => {
    // simulate requireAuth returning an HTTP 401 Response
    requireAuthMock.mockReturnValue(new Response('Unauthorized', { status: 401 }));

    const res = await PUT(new Request('http://localhost/api/absences/1'), makeCtx(PUT, { id: '1' }));

    expect(res.status).toBe(401);
    expect(updateAbsenceFeedMock).not.toHaveBeenCalled();
    });
  it('PUT returns 404 when absencee not found', async () => {
    updateAbsenceFeedMock.mockResolvedValue({ absence: null });

    const res = await PUT(
      new Request('http://localhost/api/absences/999', { method: 'PUT', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ TCS: 'Bob' }) }),
      makeCtx(PUT, { id: '999' }),
    );

    expect(res.status).toBe(404);
    const body = await res.json();
    expect(body.message).toBe('Absence not found');
  });

  it('DELETE returns 200 on success', async () => {
    deleteAbsenceFeedMock.mockResolvedValue({ absence: { id_absence: 123, id_salarie: 955 } });

    const res = await DELETE(new Request('http://localhost/api/absences/123'), makeCtx(DELETE, { id: '123' }));

    expect(res.status).toBe(200);
    const body = await res.json();
    expect(body).toEqual({ message: 'Absence deleted successfully', absence: { id_absence: 123, id_salarie: 955 } });
  });
   it('DELETE returns 400 when id param is invalid', async () => {
    const res = await DELETE(new Request('http://localhost/api/absences/abc'), makeCtx(DELETE, { id: 'abc' }));

    expect(res.status).toBe(400);
    const body = await res.json();
    expect(body.message).toBe('Invalid ID parameter');
  });
  it('DELETE returns auth error when request is unauthorized', async () => {
    // simulate requireAuth returning an HTTP 401 Response
    requireAuthMock.mockReturnValue(new Response('Unauthorized', { status: 401 }));

    const res = await DELETE(new Request('http://localhost/api/absences/1'), makeCtx(DELETE, { id: '1' }));

    expect(res.status).toBe(401);
    expect(deleteAbsenceFeedMock).not.toHaveBeenCalled();
    });
  it('DELETE returns 404 when not found', async () => {
    deleteAbsenceFeedMock.mockResolvedValue({ absence: null });

    const res = await DELETE(new Request('http://localhost/api/absences/999'), makeCtx(DELETE, { id: '999' }));

    expect(res.status).toBe(404);
    const body = await res.json();
    expect(body.message).toBe('Absence not found');
  });
  it('DELETE returns 500 for unknown errors', async () => {
    deleteAbsenceFeedMock.mockRejectedValue(new Error('boom'));

    const res = await DELETE(new Request('http://localhost/api/absences/123'), makeCtx(DELETE, { id: '123' }));

    expect(res.status).toBe(500);
    const body = await res.json();
    expect(body.message).toMatch(/Internal Server Error/i);
  });
});
