/**
 * Tests for ClerkAuthGenerator
 */

import { ClerkAuthGenerator } from './clerk-generator';
import { GeneratorContext } from '../types';

describe('ClerkAuthGenerator', () => {
  let generator: ClerkAuthGenerator;
  let context: GeneratorContext;

  beforeEach(() => {
    generator = new ClerkAuthGenerator();
    context = {
      workflow: {
        workflow: {
          id: 'test-clerk-app',
          name: 'Test Clerk App',
          version: '1.0.0',
          description: 'Test app with Clerk auth',
        },
        stack: {},
      } as any,
      executionPlan: {} as any,
      outputDirectory: '/test/output',
    };
  });

  it('should have correct metadata', () => {
    expect(generator.id).toBe('clerk-auth');
    expect(generator.name).toBe('Clerk Authentication Generator');
    expect(generator.description).toBe('Generates Clerk authentication integration');
  });

  it('should generate both backend and frontend files', () => {
    const result = generator.generate(context);

    expect(result.files.length).toBeGreaterThan(0);

    // Backend files
    const middlewareFile = result.files.find(f => f.path.includes('backend/src/middleware/clerk-auth.ts'));
    expect(middlewareFile).toBeDefined();

    const typesFile = result.files.find(f => f.path.includes('backend/types/clerk.d.ts'));
    expect(typesFile).toBeDefined();

    // Frontend files
    const authProviderFile = result.files.find(f => f.path.includes('frontend/src/components/AuthProvider.tsx'));
    expect(authProviderFile).toBeDefined();

    const loginPageFile = result.files.find(f => f.path.includes('frontend/src/pages/Login.tsx'));
    expect(loginPageFile).toBeDefined();

    const signupPageFile = result.files.find(f => f.path.includes('frontend/src/pages/Signup.tsx'));
    expect(signupPageFile).toBeDefined();

    const hooksFile = result.files.find(f => f.path.includes('frontend/src/hooks/useAuth.ts'));
    expect(hooksFile).toBeDefined();

    const protectedRouteFile = result.files.find(f => f.path.includes('frontend/src/components/ProtectedRoute.tsx'));
    expect(protectedRouteFile).toBeDefined();
  });

  it('should generate Clerk middleware with authentication functions', () => {
    const result = generator.generate(context);
    const middlewareFile = result.files.find(f => f.path.includes('clerk-auth.ts'));

    expect(middlewareFile).toBeDefined();
    expect(middlewareFile?.content).toContain("import { clerkMiddleware, requireAuth, getAuth } from '@clerk/express'");
    expect(middlewareFile?.content).toContain('export const clerkAuth = clerkMiddleware()');
    expect(middlewareFile?.content).toContain('export const authenticate = requireAuth()');
    expect(middlewareFile?.content).toContain('export const authorize = (allowedRoles: string[])');
    expect(middlewareFile?.content).toContain('export const getCurrentUserId');
    expect(middlewareFile?.content).toContain('export const clerkErrorHandler');
  });

  it('should generate TypeScript types for Express request', () => {
    const result = generator.generate(context);
    const typesFile = result.files.find(f => f.path.includes('clerk.d.ts'));

    expect(typesFile).toBeDefined();
    expect(typesFile?.content).toContain('namespace Express');
    expect(typesFile?.content).toContain('interface Request');
    expect(typesFile?.content).toContain('userId: string | null');
    expect(typesFile?.content).toContain('sessionId: string | null');
    expect(typesFile?.content).toContain('sessionClaims: any');
  });

  it('should generate AuthProvider with environment variable check', () => {
    const result = generator.generate(context, {
      afterSignInUrl: '/dashboard',
      afterSignUpUrl: '/welcome',
    });
    const providerFile = result.files.find(f => f.path.includes('AuthProvider.tsx'));

    expect(providerFile).toBeDefined();
    expect(providerFile?.content).toContain("import { ClerkProvider } from '@clerk/clerk-react'");
    expect(providerFile?.content).toContain('import.meta.env.VITE_CLERK_PUBLISHABLE_KEY');
    expect(providerFile?.content).toContain('Missing Clerk Publishable Key');
    expect(providerFile?.content).toContain('afterSignInUrl="/dashboard"');
    expect(providerFile?.content).toContain('afterSignUpUrl="/welcome"');
  });

  it('should use default redirect URLs when not provided', () => {
    const result = generator.generate(context);
    const providerFile = result.files.find(f => f.path.includes('AuthProvider.tsx'));

    expect(providerFile).toBeDefined();
    expect(providerFile?.content).toContain('afterSignInUrl="/"');
    expect(providerFile?.content).toContain('afterSignUpUrl="/"');
  });

  it('should generate Login page with Clerk SignIn component', () => {
    const result = generator.generate(context);
    const loginFile = result.files.find(f => f.path.includes('Login.tsx'));

    expect(loginFile).toBeDefined();
    expect(loginFile?.content).toContain("import { SignIn } from '@clerk/clerk-react'");
    expect(loginFile?.content).toContain('<SignIn');
    expect(loginFile?.content).toContain('routing="path"');
    expect(loginFile?.content).toContain('path="/login"');
    expect(loginFile?.content).toContain('signUpUrl="/signup"');
  });

  it('should generate Signup page with Clerk SignUp component', () => {
    const result = generator.generate(context);
    const signupFile = result.files.find(f => f.path.includes('Signup.tsx'));

    expect(signupFile).toBeDefined();
    expect(signupFile?.content).toContain("import { SignUp } from '@clerk/clerk-react'");
    expect(signupFile?.content).toContain('<SignUp');
    expect(signupFile?.content).toContain('routing="path"');
    expect(signupFile?.content).toContain('path="/signup"');
    expect(signupFile?.content).toContain('signInUrl="/login"');
  });

  it('should generate auth hooks for user, auth functions, and roles', () => {
    const result = generator.generate(context);
    const hooksFile = result.files.find(f => f.path.includes('useAuth.ts'));

    expect(hooksFile).toBeDefined();
    expect(hooksFile?.content).toContain("import { useUser, useAuth, useClerk } from '@clerk/clerk-react'");
    expect(hooksFile?.content).toContain('export const useCurrentUser');
    expect(hooksFile?.content).toContain('export const useAuthFunctions');
    expect(hooksFile?.content).toContain('export const useHasRole');
    expect(hooksFile?.content).toContain('user, isLoaded, isSignedIn');
    expect(hooksFile?.content).toContain('signOut');
    expect(hooksFile?.content).toContain('getToken');
  });

  it('should generate ProtectedRoute component with role support', () => {
    const result = generator.generate(context);
    const protectedRouteFile = result.files.find(f => f.path.includes('ProtectedRoute.tsx'));

    expect(protectedRouteFile).toBeDefined();
    expect(protectedRouteFile?.content).toContain("import { useAuth } from '@clerk/clerk-react'");
    expect(protectedRouteFile?.content).toContain("import { Navigate } from 'react-router-dom'");
    expect(protectedRouteFile?.content).toContain('interface ProtectedRouteProps');
    expect(protectedRouteFile?.content).toContain('requiredRoles?: string[]');
    expect(protectedRouteFile?.content).toContain('if (!isLoaded)');
    expect(protectedRouteFile?.content).toContain('if (!isSignedIn)');
    expect(protectedRouteFile?.content).toContain('<Navigate to="/login"');
  });

  it('should generate environment variable template', () => {
    const envTemplate = generator.generateEnvTemplate();

    expect(envTemplate).toContain('# Clerk Authentication');
    expect(envTemplate).toContain('CLERK_SECRET_KEY=');
    expect(envTemplate).toContain('VITE_CLERK_PUBLISHABLE_KEY=');
  });

  it('should return correct backend dependencies', () => {
    const deps = generator.getBackendDependencies();

    expect(deps['@clerk/express']).toBeDefined();
    expect(deps['@clerk/express']).toMatch(/\^1\.\d+\.\d+/);
  });

  it('should return correct frontend dependencies', () => {
    const deps = generator.getFrontendDependencies();

    expect(deps['@clerk/clerk-react']).toBeDefined();
    expect(deps['@clerk/clerk-react']).toMatch(/\^5\.\d+\.\d+/);
  });

  it('should set overwrite flag to true for all generated files', () => {
    const result = generator.generate(context);

    result.files.forEach(file => {
      expect(file.overwrite).toBe(true);
    });
  });

  it('should handle custom config options', () => {
    const config = {
      features: ['email', 'google', 'github'],
      afterSignInUrl: '/home',
      afterSignUpUrl: '/onboarding',
    };

    const result = generator.generate(context, config);

    expect(result.files.length).toBeGreaterThan(0);

    const providerFile = result.files.find(f => f.path.includes('AuthProvider.tsx'));
    expect(providerFile?.content).toContain('afterSignInUrl="/home"');
    expect(providerFile?.content).toContain('afterSignUpUrl="/onboarding"');
  });

  it('should generate role-based authorization middleware', () => {
    const result = generator.generate(context);
    const middlewareFile = result.files.find(f => f.path.includes('clerk-auth.ts'));

    expect(middlewareFile).toBeDefined();
    expect(middlewareFile?.content).toContain('const userRoles = (auth.sessionClaims?.metadata as any)?.roles || []');
    expect(middlewareFile?.content).toContain('if (!allowedRoles.some(role => userRoles.includes(role)))');
    expect(middlewareFile?.content).toContain("return res.status(403).json({ message: 'Insufficient permissions' })");
  });

  it('should handle error scenarios in middleware', () => {
    const result = generator.generate(context);
    const middlewareFile = result.files.find(f => f.path.includes('clerk-auth.ts'));

    expect(middlewareFile).toBeDefined();
    expect(middlewareFile?.content).toContain("return res.status(401).json({ message: 'Not authenticated' })");
    expect(middlewareFile?.content).toContain("return res.status(403).json({ message: 'Insufficient permissions' })");
    expect(middlewareFile?.content).toContain('clerkErrorHandler');
    expect(middlewareFile?.content).toContain('console.error');
  });

  it('should generate proper file paths relative to output directory', () => {
    const result = generator.generate(context);

    result.files.forEach(file => {
      expect(file.path).toContain('/test/output');
    });

    // Check specific paths
    const middlewareFile = result.files.find(f => f.path.includes('clerk-auth.ts'));
    expect(middlewareFile?.path).toBe('/test/output/backend/src/middleware/clerk-auth.ts');

    const authProviderFile = result.files.find(f => f.path.includes('AuthProvider.tsx'));
    expect(authProviderFile?.path).toBe('/test/output/frontend/src/components/AuthProvider.tsx');
  });
});
