import Fastify, { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
import { Pool } from 'pg';
import adminGuardPlugin, { setAdminGuardPool } from '../../../../core/plugins/admin-guard.plugin';

const createReply = (): Partial<FastifyReply> & { payload?: unknown; statusCode?: number } => {
  return {
    sent: false,
    code: jest.fn(function(this: any, status: number) {
      this.statusCode = status;
      return this;
    }),
    send: jest.fn(function(this: any, payload: unknown) {
      this.payload = payload;
      this.sent = true;
      return this;
    }),
  };
};

describe('admin guard plugin', () => {
  let fastify: FastifyInstance;
  let authenticateMock: jest.Mock;
  let mockPool: { query: jest.Mock };

  beforeEach(async () => {
    fastify = Fastify();
    authenticateMock = jest.fn(async (request: FastifyRequest) => {
      request.userContext = {
        userId: 'auth0|admin',
        email: 'admin@motovaultpro.com',
        emailVerified: true,
        onboardingCompleted: true,
        isAdmin: false,
        subscriptionTier: 'free',
      };
    });
    fastify.decorate('authenticate', authenticateMock);

    await fastify.register(adminGuardPlugin);

    mockPool = {
      query: jest.fn().mockResolvedValue({
        rows: [{
          auth0_sub: 'auth0|admin',
          email: 'admin@motovaultpro.com',
          role: 'admin',
          revoked_at: null,
        }],
      }),
    };

    setAdminGuardPool(mockPool as unknown as Pool);
  });

  afterEach(async () => {
    await fastify.close();
    setAdminGuardPool(null as unknown as Pool);
    jest.clearAllMocks();
  });

  it('authenticates the request before enforcing admin access', async () => {
    const request = {} as FastifyRequest;
    const reply = createReply();

    await fastify.requireAdmin(request, reply as FastifyReply);

    expect(authenticateMock).toHaveBeenCalledTimes(1);
    expect(mockPool.query).toHaveBeenCalledTimes(1);
    expect(request.userContext?.isAdmin).toBe(true);
    expect(reply.code).not.toHaveBeenCalled();
    expect(reply.send).not.toHaveBeenCalled();
  });

  it('rejects non-admin users with 403', async () => {
    mockPool.query.mockResolvedValue({ rows: [] });
    const request = {} as FastifyRequest;
    const reply = createReply();

    await fastify.requireAdmin(request, reply as FastifyReply);

    expect(authenticateMock).toHaveBeenCalledTimes(1);
    expect(mockPool.query).toHaveBeenCalledTimes(1);
    expect(reply.code).toHaveBeenCalledWith(403);
    expect(reply.send).toHaveBeenCalledWith({
      error: 'Forbidden',
      message: 'Admin access required',
    });
  });

  it('responds with 500 when database pool is not initialized', async () => {
    setAdminGuardPool(null as unknown as Pool);
    const request = {} as FastifyRequest;
    const reply = createReply();

    await fastify.requireAdmin(request, reply as FastifyReply);

    expect(reply.code).toHaveBeenCalledWith(500);
    expect(reply.send).toHaveBeenCalledWith({
      error: 'Internal server error',
      message: 'Admin check unavailable',
    });
  });
});
