/**
 * SessionGateway tests
 *
 * TDD: All tests mock cookies() from 'next/headers' and global.fetch
 */

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { SessionGateway } from './SessionGateway';
import type { AuthSessionDTO } from '../types/generated/AuthSessionDTO';

// Mock next/headers
vi.mock('next/headers', () => ({
  cookies: vi.fn(),
}));

// Mock global.fetch
const mockFetch = vi.fn();
global.fetch = mockFetch;

describe('SessionGateway', () => {
  let gateway: SessionGateway;
  let mockCookies: ReturnType<typeof vi.mocked>;

  beforeEach(async () => {
    vi.clearAllMocks();
    const nextHeaders = await import('next/headers');
    mockCookies = vi.mocked(nextHeaders.cookies);
    gateway = new SessionGateway();
  });

  describe('getSession()', () => {
    it('should return null when no cookies are present', async () => {
      // Arrange
      mockCookies.mockReturnValue({
        toString: () => '',
      } as any);

      // Act
      const result = await gateway.getSession();

      // Assert
      expect(result).toBeNull();
      expect(mockFetch).not.toHaveBeenCalled();
    });

    it('should return session object when valid gp_session cookie exists', async () => {
      // Arrange
      const mockSession: AuthSessionDTO = {
        token: 'valid-token',
        user: {
          userId: 'user-123',
          email: 'test@example.com',
          displayName: 'Test User',
          role: 'driver',
        },
      };

      mockCookies.mockReturnValue({
        toString: () => 'gp_session=valid-token; other=value',
      } as any);

      mockFetch.mockResolvedValueOnce({
        ok: true,
        status: 200,
        json: async () => mockSession,
      } as Response);

      // Act
      const result = await gateway.getSession();

      // Assert
      expect(result).toEqual(mockSession);
      expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining('/auth/session'), {
        headers: { cookie: 'gp_session=valid-token; other=value' },
        cache: 'no-store',
        credentials: 'include',
      });
    });

    it('should return null when session is invalid or expired', async () => {
      // Arrange
      mockCookies.mockReturnValue({
        toString: () => 'gp_session=expired-token',
      } as any);

      mockFetch.mockResolvedValueOnce({
        ok: false,
        status: 401,
        statusText: 'Unauthorized',
      } as Response);

      // Act
      const result = await gateway.getSession();

      // Assert
      expect(result).toBeNull();
      expect(mockFetch).toHaveBeenCalled();
    });

    it('should return null on non-2xx response', async () => {
      // Arrange
      mockCookies.mockReturnValue({
        toString: () => 'gp_session=some-token',
      } as any);

      mockFetch.mockResolvedValueOnce({
        ok: false,
        status: 500,
        statusText: 'Internal Server Error',
      } as Response);

      // Act
      const result = await gateway.getSession();

      // Assert
      expect(result).toBeNull();
      expect(mockFetch).toHaveBeenCalled();
    });

    it('should return null on network error', async () => {
      // Arrange
      mockCookies.mockReturnValue({
        toString: () => 'gp_session=some-token',
      } as any);

      mockFetch.mockRejectedValueOnce(new Error('Network error'));

      // Act
      const result = await gateway.getSession();

      // Assert
      expect(result).toBeNull();
      expect(mockFetch).toHaveBeenCalled();
    });

    it('should return null when fetch throws any error', async () => {
      // Arrange
      mockCookies.mockReturnValue({
        toString: () => 'gp_session=some-token',
      } as any);

      mockFetch.mockRejectedValueOnce(new Error('Connection timeout'));

      // Act
      const result = await gateway.getSession();

      // Assert
      expect(result).toBeNull();
    });
  });
});
