import { describe, it, expect, vi, beforeEach } from 'vitest';
import type { NextRequest } from 'next/server';

const mockGetSessionFromRequest = vi.fn();

// Mock Next.js server components
vi.mock('next/server', () => ({
  NextResponse: {
    next: vi.fn(() => ({
      headers: {
        set: vi.fn(),
      },
    })),
    redirect: vi.fn((url: URL) => ({
      headers: {
        set: vi.fn(),
      },
      url: url.toString(),
    })),
  },
}));

// Mock SessionGateway
vi.mock('@/lib/gateways/SessionGateway', () => ({
  SessionGateway: class {
    getSessionFromRequest = mockGetSessionFromRequest;
  },
}));

// Mock RouteConfig to have deterministic behavior in tests
vi.mock('@/lib/routing/RouteConfig', () => ({
  routes: {
    auth: { login: '/auth/login' },
    public: { home: '/' },
    protected: { dashboard: '/dashboard' },
    sponsor: { root: '/sponsor', dashboard: '/sponsor/dashboard' },
    admin: { root: '/admin' },
  },
  routeMatchers: {
    isPublic: (path: string) => {
      return ['/', '/auth/login'].includes(path);
    },
    isInGroup: (path: string, group: string) => {
      if (group === 'admin') return path.startsWith('/admin');
      if (group === 'sponsor') return path.startsWith('/sponsor');
      return false;
    },
    requiresAuth: (path: string) => {
      return !['/', '/auth/login'].includes(path);
    },
    requiresRole: (path: string) => {
      if (path.startsWith('/admin')) return ['admin'];
      if (path.startsWith('/sponsor')) return ['sponsor'];
      return null;
    },
  },
}));

// Import middleware after mocks
import { middleware } from './middleware';

describe('Middleware - Route Protection', () => {
  let mockRequest: NextRequest;

  beforeEach(() => {
    vi.clearAllMocks();
    mockGetSessionFromRequest.mockReset();

    mockRequest = {
      url: 'http://localhost:3000',
      nextUrl: { pathname: '/' },
      method: 'GET',
      headers: {
        set: vi.fn(),
        get: vi.fn().mockReturnValue(''),
      },
    } as any;
  });

  describe('Redirect logic and returnTo', () => {
    it('should redirect unauthenticated users to login with returnTo', async () => {
      mockRequest.nextUrl.pathname = '/dashboard';
      mockGetSessionFromRequest.mockResolvedValue(null);

      const response = await middleware(mockRequest);

      expect(response.url).toContain('/auth/login');
      expect(response.url).toContain('returnTo=%2Fdashboard');
    });

    it('should allow authenticated users to access protected routes', async () => {
      mockRequest.nextUrl.pathname = '/dashboard';
      mockGetSessionFromRequest.mockResolvedValue({
        user: { userId: '123', role: 'driver' },
      });

      const response = await middleware(mockRequest);

      // Should not be a redirect (no url property on NextResponse.next() mock)
      expect(response.url).toBeUndefined();
    });
  });

  describe('Role-based redirects', () => {
    it('should redirect user with wrong role to their home page', async () => {
      // Driver trying to access admin
      mockRequest.nextUrl.pathname = '/admin';
      mockGetSessionFromRequest.mockResolvedValue({
        user: { userId: '123', role: 'driver' },
      });

      const response = await middleware(mockRequest);

      // Should redirect to dashboard (driver's home)
      expect(response.url).toBe('http://localhost:3000/dashboard');
    });

    it('should redirect sponsor with wrong role to sponsor dashboard', async () => {
      // Sponsor trying to access admin
      mockRequest.nextUrl.pathname = '/admin';
      mockGetSessionFromRequest.mockResolvedValue({
        user: { userId: '123', role: 'sponsor' },
      });

      const response = await middleware(mockRequest);

      expect(response.url).toBe('http://localhost:3000/sponsor/dashboard');
    });

    it('should allow user with correct role to pass through', async () => {
      mockRequest.nextUrl.pathname = '/admin';
      mockGetSessionFromRequest.mockResolvedValue({
        user: { userId: '123', role: 'admin' },
      });

      const response = await middleware(mockRequest);

      expect(response.url).toBeUndefined();
    });
  });

  describe('Public routes', () => {
    it('should allow access to public routes without session', async () => {
      mockRequest.nextUrl.pathname = '/';
      mockGetSessionFromRequest.mockResolvedValue(null);

      const response = await middleware(mockRequest);

      expect(response.url).toBeUndefined();
    });
  });

  describe('Special redirects', () => {
    it('should handle /sponsor root redirect', async () => {
      mockRequest.nextUrl.pathname = '/sponsor';
      mockGetSessionFromRequest.mockResolvedValue({
        user: { userId: '123', role: 'sponsor' },
      });
      
      const response = await middleware(mockRequest);
      
      expect(response.url).toBe('http://localhost:3000/sponsor/dashboard');
    });
  });
});
