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

// Hoist mock so it's available in the vi.mock factory
const { mockIntlMiddleware } = vi.hoisted(() => ({
  mockIntlMiddleware: vi.fn(() => NextResponse.next()),
}));

vi.mock('next-intl/middleware', () => ({
  default: vi.fn(() => mockIntlMiddleware),
}));

// Mock routing config
vi.mock('@/i18n/routing', () => ({
  routing: {
    locales: ['vi', 'en'],
    defaultLocale: 'vi',
    localePrefix: 'as-needed',
  },
}));

import { middleware, config } from '../middleware';

function createRequest(
  pathname: string,
  options?: { cookies?: Record<string, string>; origin?: string },
): NextRequest {
  const origin = options?.origin ?? 'http://localhost:3200';
  const url = new URL(pathname, origin);
  const request = new NextRequest(url);
  if (options?.cookies) {
    for (const [name, value] of Object.entries(options.cookies)) {
      request.cookies.set(name, value);
    }
  }
  return request;
}

describe('middleware', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockIntlMiddleware.mockReturnValue(NextResponse.next());
  });

  describe('matcher config', () => {
    it('exports a valid matcher that excludes api and static routes', () => {
      expect(config).toBeDefined();
      expect(config.matcher).toEqual([
        '/((?!api|_next/static|_next/image|favicon.ico|public).*)',
      ]);
    });

    it('matcher pattern matches normal routes', () => {
      const pattern = new RegExp(config.matcher[0]);
      expect(pattern.test('/dashboard')).toBe(true);
      expect(pattern.test('/login')).toBe(true);
      expect(pattern.test('/vi/search')).toBe(true);
    });
  });

  describe('public paths — unauthenticated access', () => {
    it('allows unauthenticated access to the root path /', () => {
      const request = createRequest('/');
      const response = middleware(request);
      // Should not redirect to login — should pass through to intlMiddleware
      expect(response.headers.get('location')).toBeNull();
    });

    it('allows unauthenticated access to /login', () => {
      const request = createRequest('/login');
      const response = middleware(request);
      expect(response.headers.get('location')).toBeNull();
    });

    it('allows unauthenticated access to /register', () => {
      const request = createRequest('/register');
      const response = middleware(request);
      expect(response.headers.get('location')).toBeNull();
    });

    it('allows unauthenticated access to /search', () => {
      const request = createRequest('/search');
      const response = middleware(request);
      expect(response.headers.get('location')).toBeNull();
    });

    it('allows unauthenticated access to /search/anything', () => {
      const request = createRequest('/search/filters?type=APARTMENT');
      const response = middleware(request);
      expect(response.headers.get('location')).toBeNull();
    });

    it('allows unauthenticated access to /listings', () => {
      const request = createRequest('/listings');
      const response = middleware(request);
      expect(response.headers.get('location')).toBeNull();
    });

    it('allows unauthenticated access to /listings/:id', () => {
      const request = createRequest('/listings/abc-123');
      const response = middleware(request);
      expect(response.headers.get('location')).toBeNull();
    });

    it('allows unauthenticated access to /auth/callback', () => {
      const request = createRequest('/auth/callback');
      const response = middleware(request);
      expect(response.headers.get('location')).toBeNull();
    });

    it('allows unauthenticated access to /auth/callback/google', () => {
      const request = createRequest('/auth/callback/google');
      const response = middleware(request);
      expect(response.headers.get('location')).toBeNull();
    });
  });

  describe('auth redirect — protected routes require authentication', () => {
    it('redirects unauthenticated users from /dashboard to /login', () => {
      const request = createRequest('/dashboard');
      const response = middleware(request);
      const location = response.headers.get('location');
      expect(location).toBeTruthy();
      const redirectUrl = new URL(location!);
      expect(redirectUrl.pathname).toBe('/login');
      expect(redirectUrl.searchParams.get('redirect')).toBe('/dashboard');
    });

    it('redirects unauthenticated users from /profile to /login', () => {
      const request = createRequest('/profile');
      const response = middleware(request);
      const location = response.headers.get('location');
      expect(location).toBeTruthy();
      const redirectUrl = new URL(location!);
      expect(redirectUrl.pathname).toBe('/login');
      expect(redirectUrl.searchParams.get('redirect')).toBe('/profile');
    });

    it('redirects unauthenticated users from /my-listings to /login with redirect param', () => {
      const request = createRequest('/my-listings');
      const response = middleware(request);
      const location = response.headers.get('location');
      expect(location).toBeTruthy();
      const redirectUrl = new URL(location!);
      expect(redirectUrl.pathname).toBe('/login');
      expect(redirectUrl.searchParams.get('redirect')).toBe('/my-listings');
    });

    it('allows authenticated users to access /dashboard', () => {
      const request = createRequest('/dashboard', {
        cookies: { goodgo_authenticated: 'true' },
      });
      const response = middleware(request);
      expect(response.headers.get('location')).toBeNull();
    });

    it('allows authenticated users to access /profile', () => {
      const request = createRequest('/profile', {
        cookies: { goodgo_authenticated: 'true' },
      });
      const response = middleware(request);
      expect(response.headers.get('location')).toBeNull();
    });
  });

  describe('auth-only redirect — authenticated users bounce from login/register', () => {
    it('redirects authenticated users from /login to /dashboard', () => {
      const request = createRequest('/login', {
        cookies: { goodgo_authenticated: 'true' },
      });
      const response = middleware(request);
      const location = response.headers.get('location');
      expect(location).toBeTruthy();
      const redirectUrl = new URL(location!);
      expect(redirectUrl.pathname).toBe('/dashboard');
    });

    it('redirects authenticated users from /register to /dashboard', () => {
      const request = createRequest('/register', {
        cookies: { goodgo_authenticated: 'true' },
      });
      const response = middleware(request);
      const location = response.headers.get('location');
      expect(location).toBeTruthy();
      const redirectUrl = new URL(location!);
      expect(redirectUrl.pathname).toBe('/dashboard');
    });

    it('does NOT redirect authenticated users from other public paths', () => {
      const request = createRequest('/search', {
        cookies: { goodgo_authenticated: 'true' },
      });
      const response = middleware(request);
      expect(response.headers.get('location')).toBeNull();
    });
  });

  describe('locale stripping — paths with locale prefix', () => {
    it('strips /vi prefix and allows public path', () => {
      const request = createRequest('/vi/login');
      const response = middleware(request);
      // /login is public, no redirect to /login (would be self-redirect)
      expect(response.headers.get('location')).toBeNull();
    });

    it('strips /en prefix and allows public path', () => {
      const request = createRequest('/en/search');
      const response = middleware(request);
      expect(response.headers.get('location')).toBeNull();
    });

    it('strips /vi prefix and redirects protected path to login', () => {
      const request = createRequest('/vi/dashboard');
      const response = middleware(request);
      const location = response.headers.get('location');
      expect(location).toBeTruthy();
      const redirectUrl = new URL(location!);
      expect(redirectUrl.pathname).toBe('/login');
      expect(redirectUrl.searchParams.get('redirect')).toBe('/dashboard');
    });

    it('strips /en prefix and redirects protected path to login', () => {
      const request = createRequest('/en/profile');
      const response = middleware(request);
      const location = response.headers.get('location');
      expect(location).toBeTruthy();
      const redirectUrl = new URL(location!);
      expect(redirectUrl.pathname).toBe('/login');
      expect(redirectUrl.searchParams.get('redirect')).toBe('/profile');
    });

    it('strips /vi prefix for auth-only redirect for authenticated users', () => {
      const request = createRequest('/vi/login', {
        cookies: { goodgo_authenticated: 'true' },
      });
      const response = middleware(request);
      const location = response.headers.get('location');
      expect(location).toBeTruthy();
      const redirectUrl = new URL(location!);
      expect(redirectUrl.pathname).toBe('/dashboard');
    });

    it('strips /en prefix for auth-only redirect for authenticated users', () => {
      const request = createRequest('/en/register', {
        cookies: { goodgo_authenticated: 'true' },
      });
      const response = middleware(request);
      const location = response.headers.get('location');
      expect(location).toBeTruthy();
      const redirectUrl = new URL(location!);
      expect(redirectUrl.pathname).toBe('/dashboard');
    });
  });

  describe('intl middleware delegation', () => {
    it('delegates to intlMiddleware for authenticated public paths', () => {
      const request = createRequest('/search', {
        cookies: { goodgo_authenticated: 'true' },
      });
      middleware(request);
      expect(mockIntlMiddleware).toHaveBeenCalledWith(request);
    });

    it('delegates to intlMiddleware for unauthenticated public paths', () => {
      const request = createRequest('/');
      middleware(request);
      expect(mockIntlMiddleware).toHaveBeenCalledWith(request);
    });

    it('delegates to intlMiddleware for authenticated protected paths', () => {
      const request = createRequest('/dashboard', {
        cookies: { goodgo_authenticated: 'true' },
      });
      middleware(request);
      expect(mockIntlMiddleware).toHaveBeenCalledWith(request);
    });

    it('does NOT delegate to intlMiddleware when redirecting to login', () => {
      const request = createRequest('/dashboard');
      middleware(request);
      expect(mockIntlMiddleware).not.toHaveBeenCalled();
    });

    it('does NOT delegate to intlMiddleware when redirecting to dashboard', () => {
      const request = createRequest('/login', {
        cookies: { goodgo_authenticated: 'true' },
      });
      middleware(request);
      expect(mockIntlMiddleware).not.toHaveBeenCalled();
    });
  });

  describe('edge cases', () => {
    it('handles path with trailing slash', () => {
      const request = createRequest('/dashboard/');
      const response = middleware(request);
      const location = response.headers.get('location');
      expect(location).toBeTruthy();
      const redirectUrl = new URL(location!);
      expect(redirectUrl.pathname).toBe('/login');
    });

    it('handles deeply nested protected path', () => {
      const request = createRequest('/dashboard/analytics/reports');
      const response = middleware(request);
      const location = response.headers.get('location');
      expect(location).toBeTruthy();
      const redirectUrl = new URL(location!);
      expect(redirectUrl.pathname).toBe('/login');
      expect(redirectUrl.searchParams.get('redirect')).toBe(
        '/dashboard/analytics/reports',
      );
    });

    it('handles root path with locale prefix /vi', () => {
      const request = createRequest('/vi');
      const response = middleware(request);
      // /vi -> stripped to / -> public, no redirect
      expect(response.headers.get('location')).toBeNull();
    });

    it('handles root path with locale prefix /en', () => {
      const request = createRequest('/en');
      const response = middleware(request);
      expect(response.headers.get('location')).toBeNull();
    });

    it('does not treat partial locale match as locale prefix', () => {
      // /video should NOT strip "vi" prefix — "vi" must be followed by / or end
      const request = createRequest('/video');
      const response = middleware(request);
      // /video is not public, so should redirect to login
      const location = response.headers.get('location');
      expect(location).toBeTruthy();
      const redirectUrl = new URL(location!);
      expect(redirectUrl.pathname).toBe('/login');
    });

    it('preserves redirect path through query string in login redirect', () => {
      const request = createRequest('/settings?tab=profile');
      const response = middleware(request);
      const location = response.headers.get('location');
      expect(location).toBeTruthy();
      const redirectUrl = new URL(location!);
      expect(redirectUrl.pathname).toBe('/login');
      // The redirect param should contain the stripped path
      expect(redirectUrl.searchParams.get('redirect')).toBe('/settings');
    });

    it('handles cookie with empty string value as having the cookie', () => {
      const request = createRequest('/dashboard', {
        cookies: { goodgo_authenticated: '' },
      });
      const response = middleware(request);
      // cookies.has() returns true even with empty value
      expect(response.headers.get('location')).toBeNull();
    });
  });
});
