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

const { requireAuthMock, getSecteurByIdMock, updateSecteurMock, deleteSecteurMock } = vi.hoisted(() => ({
  requireAuthMock: vi.fn(),
  getSecteurByIdMock: vi.fn(),
  updateSecteurMock: vi.fn(),
  deleteSecteurMock: vi.fn(),
}));

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

vi.mock('@/services/reference-lookups.service', () => ({
  getSecteurById: getSecteurByIdMock,
  updateSecteur: updateSecteurMock,
  deleteSecteur: deleteSecteurMock,
}));

import { GET, PUT, PATCH, DELETE } from '../../../../src/app/api/secteurs/[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/secteur/[id] route', () => {
  beforeEach(() => {
    vi.resetAllMocks();
    requireAuthMock.mockReturnValue(null);
  });

  it('GET returns 200 when secteur exists', async () => {
    getSecteurByIdMock.mockResolvedValue({ id: 1, secteur: 'Test' });

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

    expect(res.status).toBe(200);
    const body = await res.json();
    expect(body).toEqual({ message: 'Secteur retrieved successfully', secteur: { id: 1, secteur: 'Test' } });
  });

  it('GET returns 400 when id param is invalid', async () => {
    const res = await GET(new Request('http://localhost/api/secteurs/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 () => {
    requireAuthMock.mockReturnValue(new Response('Unauthorized', { status: 401 }));

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

    expect(res.status).toBe(401);
    expect(getSecteurByIdMock).not.toHaveBeenCalled();
  });

  it('GET returns 404 when not found', async () => {
    getSecteurByIdMock.mockResolvedValue(null);

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

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

  it('PUT updates and returns 200', async () => {
  updateSecteurMock.mockResolvedValue({ id: 1, tse: 'Updated' });

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

    expect(res.status).toBe(200);
    const body = await res.json();
    expect(body).toEqual({
      message: 'Secteur updated successfully',
      secteur: {
        id: 1, tse: 'Updated'

      }
    });
  });

  it('PUT returns 400 when id param is invalid', async () => {
    const res = await PUT(new Request('http://localhost/api/secteurs/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/secteurs/1', { method: 'PUT', headers: { 'content-type': 'application/json' }, body: '{' }),
      makeCtx(PUT, { id: '1' }),
    );

    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 longString = 'x'.repeat(1000);

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

    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 () => {
    requireAuthMock.mockReturnValue(new Response('Unauthorized', { status: 401 }));

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

    expect(res.status).toBe(401);
    expect(updateSecteurMock).not.toHaveBeenCalled();
  });

  it('PUT returns 404 when not found', async () => {
    updateSecteurMock.mockRejectedValue(new Error('not found'));

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

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

  it('PATCH updates partially and returns 200', async () => {
    updateSecteurMock.mockResolvedValue({ id: 1, tse: 'Patched' });

    const res = await PATCH(
      new Request('http://localhost/api/secteurs/1', { method: 'PATCH', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ tse: 'New' }) }),
      makeCtx(PATCH, { id: '1' }),
    );

    expect(res.status).toBe(200);
    const body = await res.json();
    expect(body).toEqual({ message: 'Secteur patched successfully', secteur: { id: 1, tse: 'Patched' } });
  });

  it('PATCH returns 404 when not found', async () => {
    updateSecteurMock.mockRejectedValue(new Error('not found'));

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

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

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

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

  it('PATCH returns 400 for invalid JSON', async () => {
    const res = await PATCH(
      new Request('http://localhost/api/secteurs/1', { method: 'PATCH', headers: { 'content-type': 'application/json' }, body: '{' }),
      makeCtx(PATCH, { id: '1' }),
    );

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

  it('PATCH returns auth error when request is unauthorized', async () => {
    requireAuthMock.mockReturnValue(new Response('Unauthorized', { status: 401 }));

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

    expect(res.status).toBe(401);
    expect(updateSecteurMock).not.toHaveBeenCalled();
  });

  it('PATCH returns 400 when validation fails', async () => {
    const longString = 'x'.repeat(1000);

    const res = await PATCH(
      new Request('http://localhost/api/secteurs/1', { method: 'PATCH', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ tse: longString }) }),
      makeCtx(PATCH, { id: '1' }),
    );

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

  it('DELETE returns 200 on success', async () => {
    deleteSecteurMock.mockResolvedValue({ id: 1 });

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

    expect(res.status).toBe(200);
    const body = await res.json();
    expect(body).toEqual({
       message: 'Secteur deleted successfully',
      secteur: { id: 1 } });
  });

  it('DELETE returns 400 when id param is invalid', async () => {
    const res = await DELETE(new Request('http://localhost/api/secteurs/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 () => {
    requireAuthMock.mockReturnValue(new Response('Unauthorized', { status: 401 }));

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

    expect(res.status).toBe(401);
    expect(deleteSecteurMock).not.toHaveBeenCalled();
  });

  it('DELETE returns 404 when not found', async () => {
    deleteSecteurMock.mockRejectedValue(new Error('not found'));

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

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