import { resolveRouteAuth } from '../../../model/route-auth-resolver'
import type { RouteMap, RouteDefinition, MountPoint } from '../../../model/route-discovery'
import { isRouteProtectedByMiddleware, detectGlobalAuthMiddleware } from '../../../model/middleware-detector'
import type { MiddlewareAuthConfig } from '../../../model/middleware-detector'
import type { ScanFile } from '../../../shared/types'

function makeRoute(overrides: Partial<RouteDefinition>): RouteDefinition {
  return {
    filePath: 'src/routes.ts',
    methods: ['GET'],
    path: '/api/test',
    handler: 'testHandler',
    handlerLine: 5,
    framework: 'express',
    authMiddleware: [],
    rateLimiting: false,
    isPublic: false,
    inputs: [],
    ...overrides,
  }
}

function makeRouteMap(routes: RouteDefinition[]): RouteMap {
  const fileToRoutes = new Map<string, RouteDefinition[]>()
  for (const r of routes) {
    const existing = fileToRoutes.get(r.filePath) || []
    existing.push(r)
    fileToRoutes.set(r.filePath, existing)
  }
  return { routes, fileToRoutes }
}

const emptyMiddlewareConfig: MiddlewareAuthConfig = {
  hasAuthMiddleware: false,
  protectedPaths: [],
  publicPaths: [],
  matchers: [],
  context: { usesAuthProtect: false, usesGetToken: false, usesSession: false, hasPublicRoutes: false },
}

describe('Route Auth Resolver', () => {
  it('preserves direct middleware from extractors', () => {
    const route = makeRoute({ authMiddleware: ['authGuard'] })
    const result = resolveRouteAuth(
      makeRouteMap([route]), [], [], emptyMiddlewareConfig
    )
    expect(result.routes[0].authMiddleware).toContain('authGuard')
  })

  it('resolves mount-level auth', () => {
    const route = makeRoute({ path: '/api/users' })
    const mounts: MountPoint[] = [
      { path: '/api', middleware: ['authMiddleware'], line: 1, isGlobal: false, filePath: 'src/app.ts' },
    ]
    const result = resolveRouteAuth(
      makeRouteMap([route]), mounts, [], emptyMiddlewareConfig
    )
    expect(result.routes[0].authMiddleware).toContain('authMiddleware')
  })

  it('resolves global middleware', () => {
    const route = makeRoute({ path: '/anything' })
    const mounts: MountPoint[] = [
      { path: '/', middleware: ['passportAuth'], line: 1, isGlobal: true, filePath: 'src/app.ts' },
    ]
    const result = resolveRouteAuth(
      makeRouteMap([route]), mounts, [], emptyMiddlewareConfig
    )
    expect(result.routes[0].authMiddleware).toContain('passportAuth')
  })

  it('resolves Next.js middleware.ts protection', () => {
    const route = makeRoute({ path: '/api/protected' })
    const config: MiddlewareAuthConfig = {
      ...emptyMiddlewareConfig,
      hasAuthMiddleware: true,
      authType: 'clerk',
      protectedPaths: ['/api'],
      publicPaths: ['/api/public'],
    }
    const result = resolveRouteAuth(
      makeRouteMap([route]), [], [], config
    )
    expect(result.routes[0].authMiddleware.some(m => m.includes('clerk'))).toBe(true)
  })

  it('excludes public paths from middleware protection', () => {
    const route = makeRoute({ path: '/api/public/data' })
    const config: MiddlewareAuthConfig = {
      ...emptyMiddlewareConfig,
      hasAuthMiddleware: true,
      authType: 'clerk',
      protectedPaths: ['/api/:path*'],
      publicPaths: ['/api/public/:path*'],
    }
    const result = resolveRouteAuth(
      makeRouteMap([route]), [], [], config
    )
    // Public path should NOT get middleware.ts auth
    expect(result.routes[0].authMiddleware.filter(m => m.includes('clerk'))).toHaveLength(0)
  })

  it('detects throwing auth helpers in handler body', () => {
    const route = makeRoute({ filePath: 'src/api/profile.ts', handlerLine: 3 })
    const files: ScanFile[] = [
      {
        path: 'src/api/profile.ts',
        content: `import { auth } from '@clerk/nextjs'
export async function GET() {
  const userId = getCurrentUserId()
  return Response.json({ userId })
}`,
        language: 'typescript',
      },
    ]
    const result = resolveRouteAuth(
      makeRouteMap([route]), [], files, emptyMiddlewareConfig
    )
    expect(result.routes[0].authMiddleware).toContain('throwing_auth_helper')
  })

  it('does not duplicate auth middleware', () => {
    const route = makeRoute({ authMiddleware: ['authGuard'] })
    const mounts: MountPoint[] = [
      { path: '/api', middleware: ['authGuard'], line: 1, isGlobal: false, filePath: 'src/app.ts' },
    ]
    const result = resolveRouteAuth(
      makeRouteMap([route]), mounts, [], emptyMiddlewareConfig
    )
    const authCount = result.routes[0].authMiddleware.filter(m => m === 'authGuard').length
    expect(authCount).toBe(1)
  })

  it('rebuilds fileToRoutes map correctly', () => {
    const routes = [
      makeRoute({ filePath: 'a.ts', path: '/a' }),
      makeRoute({ filePath: 'a.ts', path: '/b' }),
      makeRoute({ filePath: 'c.ts', path: '/c' }),
    ]
    const result = resolveRouteAuth(
      makeRouteMap(routes), [], [], emptyMiddlewareConfig
    )
    expect(result.fileToRoutes.get('a.ts')).toHaveLength(2)
    expect(result.fileToRoutes.get('c.ts')).toHaveLength(1)
  })
})

// ==========================================================================
// Middleware Detector: isRouteProtectedByMiddleware
// ==========================================================================

describe('isRouteProtectedByMiddleware', () => {
  describe('Clerk with createRouteMatcher', () => {
    // Clerk defines public routes as regex patterns via createRouteMatcher.
    // Everything else is protected by auth().protect().
    const clerkConfig: MiddlewareAuthConfig = {
      hasAuthMiddleware: true,
      authType: 'clerk',
      protectedPaths: ['/api/**'],
      publicPaths: ['/sign-in(.*)', '/sign-up(.*)'],
      matchers: ['/((?!.*\\..*|_next).*)'],
      context: { usesAuthProtect: true, usesGetToken: false, usesSession: false, hasPublicRoutes: true },
    }

    it('marks /api/users as protected', () => {
      const r = isRouteProtectedByMiddleware('/api/users', clerkConfig)
      expect(r.isProtected).toBe(true)
    })

    it('marks /sign-in as public (Clerk regex pattern)', () => {
      const r = isRouteProtectedByMiddleware('/sign-in', clerkConfig)
      expect(r.isProtected).toBe(false)
    })

    it('marks /sign-up/sso as public (Clerk regex subpath)', () => {
      const r = isRouteProtectedByMiddleware('/sign-up/sso', clerkConfig)
      expect(r.isProtected).toBe(false)
    })

    it('marks / as not protected (no explicit match)', () => {
      const r = isRouteProtectedByMiddleware('/', clerkConfig)
      expect(r.isProtected).toBe(false)
    })
  })

  describe('NextAuth with explicit matcher', () => {
    // NextAuth: matcher IS the protection scope
    const nextAuthConfig: MiddlewareAuthConfig = {
      hasAuthMiddleware: true,
      authType: 'nextauth',
      protectedPaths: ['/dashboard/:path*', '/api/:path*'],
      publicPaths: [],
      matchers: ['/dashboard/:path*', '/api/:path*'],
      context: { usesAuthProtect: false, usesGetToken: false, usesSession: true, hasPublicRoutes: false },
    }

    it('marks /dashboard/settings as protected', () => {
      const r = isRouteProtectedByMiddleware('/dashboard/settings', nextAuthConfig)
      expect(r.isProtected).toBe(true)
    })

    it('marks /api/users as protected', () => {
      const r = isRouteProtectedByMiddleware('/api/users', nextAuthConfig)
      expect(r.isProtected).toBe(true)
    })

    it('marks / as not protected', () => {
      const r = isRouteProtectedByMiddleware('/', nextAuthConfig)
      expect(r.isProtected).toBe(false)
    })

    it('marks /about as not protected', () => {
      const r = isRouteProtectedByMiddleware('/about', nextAuthConfig)
      expect(r.isProtected).toBe(false)
    })
  })

  describe('Supabase with helper-extracted paths', () => {
    // Supabase: helper file defines isProtectedRoute and isPublicRoute
    const supabaseConfig: MiddlewareAuthConfig = {
      hasAuthMiddleware: true,
      authType: 'supabase',
      protectedPaths: ['/dashboard', '/scan'],
      publicPaths: ['/auth/', '/login', '/'],
      matchers: ['/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)'],
      context: { usesAuthProtect: false, usesGetToken: false, usesSession: true, hasPublicRoutes: true },
    }

    it('marks /dashboard as protected', () => {
      const r = isRouteProtectedByMiddleware('/dashboard', supabaseConfig)
      expect(r.isProtected).toBe(true)
    })

    it('marks /scan as protected', () => {
      const r = isRouteProtectedByMiddleware('/scan', supabaseConfig)
      expect(r.isProtected).toBe(true)
    })

    it('marks / as public', () => {
      const r = isRouteProtectedByMiddleware('/', supabaseConfig)
      expect(r.isProtected).toBe(false)
    })

    it('marks /login as public', () => {
      const r = isRouteProtectedByMiddleware('/login', supabaseConfig)
      expect(r.isProtected).toBe(false)
    })

    it('marks /auth/callback as public', () => {
      const r = isRouteProtectedByMiddleware('/auth/callback', supabaseConfig)
      expect(r.isProtected).toBe(false)
    })

    it('does not mark /pricing as protected (not in any list)', () => {
      const r = isRouteProtectedByMiddleware('/pricing', supabaseConfig)
      expect(r.isProtected).toBe(false)
    })

    it('marks /api/users as protected (API fallback)', () => {
      const r = isRouteProtectedByMiddleware('/api/users', supabaseConfig)
      expect(r.isProtected).toBe(true)
    })

    it('does not use matchers as protection when protectedPaths exist', () => {
      // /about is not in protectedPaths, publicPaths, or /api —
      // the catch-all matcher SHOULD NOT mark it as protected
      const r = isRouteProtectedByMiddleware('/about', supabaseConfig)
      expect(r.isProtected).toBe(false)
    })
  })

  describe('No middleware detected', () => {
    it('returns not protected', () => {
      const r = isRouteProtectedByMiddleware('/api/users', emptyMiddlewareConfig)
      expect(r.isProtected).toBe(false)
    })
  })
})

// ==========================================================================
// detectGlobalAuthMiddleware integration
// ==========================================================================

describe('detectGlobalAuthMiddleware', () => {
  it('detects Clerk middleware', () => {
    const files: ScanFile[] = [{
      path: 'src/middleware.ts',
      content: `import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
const isPublicRoute = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)'])
export default clerkMiddleware((auth, request) => {
  if (!isPublicRoute(request)) auth().protect()
})
export const config = { matcher: ['/((?!.*\\\\..*|_next).*)'] }`,
      language: 'typescript',
    }]
    const config = detectGlobalAuthMiddleware(files)
    expect(config.hasAuthMiddleware).toBe(true)
    expect(config.authType).toBe('clerk')
    expect(config.publicPaths).toContain('/sign-in(.*)')
    expect(config.publicPaths).toContain('/sign-up(.*)')
    expect(config.context.usesAuthProtect).toBe(true)
  })

  it('detects NextAuth middleware', () => {
    const files: ScanFile[] = [{
      path: 'middleware.ts',
      content: `export { default } from 'next-auth/middleware'
export const config = { matcher: ['/dashboard/:path*', '/api/:path*'] }`,
      language: 'typescript',
    }]
    const config = detectGlobalAuthMiddleware(files)
    expect(config.hasAuthMiddleware).toBe(true)
    expect(config.authType).toBe('nextauth')
    expect(config.protectedPaths.length).toBeGreaterThan(0)
  })

  it('detects Supabase middleware with helper import', () => {
    const files: ScanFile[] = [
      {
        path: 'src/middleware.ts',
        content: `import { updateSession } from '@/lib/supabase/middleware'
export async function middleware(request) { return await updateSession(request) }
export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'] }`,
        language: 'typescript',
      },
      {
        path: 'src/lib/supabase/middleware.ts',
        content: `import { createServerClient } from '@supabase/ssr'
export async function updateSession(request) {
  const supabase = createServerClient(process.env.URL, process.env.KEY, {})
  const { data: { user } } = await supabase.auth.getUser()
  const pathname = request.nextUrl.pathname
  const isAuthRoute = pathname.startsWith('/auth/') || pathname === '/login'
  const isPublicRoute = pathname === '/' || isAuthRoute
  const isProtectedRoute = pathname.startsWith('/dashboard') || pathname.startsWith('/scan')
  if (!user && isProtectedRoute) { return redirect('/login') }
  return response
}`,
        language: 'typescript',
      },
    ]
    const config = detectGlobalAuthMiddleware(files)
    expect(config.hasAuthMiddleware).toBe(true)
    expect(config.authType).toBe('supabase')
    expect(config.protectedPaths).toContain('/dashboard')
    expect(config.protectedPaths).toContain('/scan')
    expect(config.publicPaths).toContain('/')
    expect(config.publicPaths).toContain('/auth/')
    expect(config.publicPaths).toContain('/login')
    expect(config.context.usesSession).toBe(true)
    expect(config.context.hasPublicRoutes).toBe(true)
  })
})
