import { FastifyRequest, FastifyReply } from 'fastify';
import { requireTier } from './require-tier';

// Mock logger to suppress output during tests
jest.mock('../logging/logger', () => ({
  logger: {
    error: jest.fn(),
    warn: jest.fn(),
    debug: jest.fn(),
    info: jest.fn(),
  },
}));

const createRequest = (subscriptionTier?: string): Partial<FastifyRequest> => {
  if (subscriptionTier === undefined) {
    return { userContext: undefined };
  }
  return {
    userContext: {
      userId: 'auth0|user123456789',
      email: 'user@example.com',
      emailVerified: true,
      onboardingCompleted: true,
      isAdmin: false,
      subscriptionTier: subscriptionTier as any,
    },
  };
};

const createReply = (): Partial<FastifyReply> & { statusCode?: number; payload?: unknown } => {
  const reply: any = {
    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;
    }),
  };
  return reply;
};

describe('requireTier middleware', () => {
  afterEach(() => {
    jest.clearAllMocks();
  });

  describe('pro user passes fuelLog.receiptScan check', () => {
    it('allows pro user through without sending a response', async () => {
      const handler = requireTier('fuelLog.receiptScan');
      const request = createRequest('pro');
      const reply = createReply();

      await handler(request as FastifyRequest, reply as FastifyReply);

      expect(reply.code).not.toHaveBeenCalled();
      expect(reply.send).not.toHaveBeenCalled();
    });
  });

  describe('enterprise user passes all checks (tier inheritance)', () => {
    it('allows enterprise user access to pro-gated features', async () => {
      const handler = requireTier('fuelLog.receiptScan');
      const request = createRequest('enterprise');
      const reply = createReply();

      await handler(request as FastifyRequest, reply as FastifyReply);

      expect(reply.code).not.toHaveBeenCalled();
      expect(reply.send).not.toHaveBeenCalled();
    });

    it('allows enterprise user access to document.scanMaintenanceSchedule', async () => {
      const handler = requireTier('document.scanMaintenanceSchedule');
      const request = createRequest('enterprise');
      const reply = createReply();

      await handler(request as FastifyRequest, reply as FastifyReply);

      expect(reply.code).not.toHaveBeenCalled();
      expect(reply.send).not.toHaveBeenCalled();
    });

    it('allows enterprise user access to vehicle.vinDecode', async () => {
      const handler = requireTier('vehicle.vinDecode');
      const request = createRequest('enterprise');
      const reply = createReply();

      await handler(request as FastifyRequest, reply as FastifyReply);

      expect(reply.code).not.toHaveBeenCalled();
      expect(reply.send).not.toHaveBeenCalled();
    });
  });

  describe('free user blocked with 403 and correct response body', () => {
    it('blocks free user from fuelLog.receiptScan', async () => {
      const handler = requireTier('fuelLog.receiptScan');
      const request = createRequest('free');
      const reply = createReply();

      await handler(request as FastifyRequest, reply as FastifyReply);

      expect(reply.code).toHaveBeenCalledWith(403);
      expect(reply.send).toHaveBeenCalledWith(
        expect.objectContaining({
          error: 'TIER_REQUIRED',
          requiredTier: 'pro',
          currentTier: 'free',
          featureName: 'Receipt Scan',
          upgradePrompt: expect.any(String),
        }),
      );
    });

    it('blocks free user from document.scanMaintenanceSchedule', async () => {
      const handler = requireTier('document.scanMaintenanceSchedule');
      const request = createRequest('free');
      const reply = createReply();

      await handler(request as FastifyRequest, reply as FastifyReply);

      expect(reply.code).toHaveBeenCalledWith(403);
      expect(reply.send).toHaveBeenCalledWith(
        expect.objectContaining({
          error: 'TIER_REQUIRED',
          requiredTier: 'pro',
          currentTier: 'free',
          featureName: 'Scan for Maintenance Schedule',
          upgradePrompt: expect.any(String),
        }),
      );
    });

    it('response body includes all required fields', async () => {
      const handler = requireTier('fuelLog.receiptScan');
      const request = createRequest('free');
      const reply = createReply();

      await handler(request as FastifyRequest, reply as FastifyReply);

      const body = (reply.send as jest.Mock).mock.calls[0][0];
      expect(body).toHaveProperty('requiredTier', 'pro');
      expect(body).toHaveProperty('currentTier', 'free');
      expect(body).toHaveProperty('featureName', 'Receipt Scan');
      expect(body).toHaveProperty('upgradePrompt');
      expect(typeof body.upgradePrompt).toBe('string');
      expect(body.upgradePrompt.length).toBeGreaterThan(0);
    });
  });

  describe('unknown feature key returns 500', () => {
    it('returns 500 INTERNAL_ERROR for unregistered feature', async () => {
      const handler = requireTier('unknown.nonexistent.feature');
      const request = createRequest('pro');
      const reply = createReply();

      await handler(request as FastifyRequest, reply as FastifyReply);

      expect(reply.code).toHaveBeenCalledWith(500);
      expect(reply.send).toHaveBeenCalledWith(
        expect.objectContaining({
          error: 'INTERNAL_ERROR',
          message: 'Unknown feature configuration',
        }),
      );
    });
  });

  describe('missing user.tier on request returns 403', () => {
    it('defaults to free tier when userContext is undefined', async () => {
      const handler = requireTier('fuelLog.receiptScan');
      const request = createRequest(); // no tier = undefined userContext
      const reply = createReply();

      await handler(request as FastifyRequest, reply as FastifyReply);

      expect(reply.code).toHaveBeenCalledWith(403);
      expect(reply.send).toHaveBeenCalledWith(
        expect.objectContaining({
          error: 'TIER_REQUIRED',
          currentTier: 'free',
          requiredTier: 'pro',
        }),
      );
    });
  });
});
