/**
 * tRPC Router Analyzer
 * Analyzes tRPC router definitions to understand which procedures are protected
 * by middleware (e.g., adminProcedure, protectedProcedure).
 * 
 * This helps prevent false positives when frontend code calls protected tRPC
 * procedures - the backend already handles authorization.
 */

import type { ScanFile } from '../shared/types'

export interface TRPCMiddleware {
  /** Name of the middleware/procedure base */
  name: string
  /** Type of protection */
  type: 'admin' | 'authenticated' | 'public' | 'unknown'
  /** File where defined */
  definedIn?: string
  /** Line number where defined */
  lineNumber?: number
}

export interface TRPCProcedure {
  /** Procedure name (e.g., 'getUsers', 'createPost') */
  name: string
  /** Router this procedure belongs to */
  router: string
  /** Whether this procedure has auth middleware */
  hasAuthMiddleware: boolean
  /** Type of middleware protection */
  middlewareType?: 'admin' | 'authenticated' | 'public'
  /** File where defined */
  definedIn?: string
}

export interface TRPCRouter {
  /** Router name (e.g., 'admin', 'user', 'post') */
  name: string
  /** File where router is defined */
  file: string
  /** Procedures in this router */
  procedures: string[]
  /** Default middleware for this router (if any) */
  defaultMiddleware?: string
  /** Whether all procedures in this router are protected */
  isProtected: boolean
}

export interface TRPCRouterContext {
  /** All detected routers */
  routers: Map<string, TRPCRouter>
  /** All detected procedures with their protection status */
  procedures: Map<string, TRPCProcedure>
  /** All detected middleware definitions */
  middlewares: Map<string, TRPCMiddleware>
  /** Whether tRPC is detected in the project */
  hasTRPC: boolean
}

/**
 * Patterns that indicate admin-level middleware
 */
const ADMIN_MIDDLEWARE_PATTERNS = [
  /adminProcedure/i,
  /adminMiddleware/i,
  /requireAdmin/i,
  /isAdmin\s*:\s*true/i,
  /role\s*===?\s*['"]admin['"]/i,
  /\.admin\s*\(/i,
  /adminRouter/i,
  /superAdminProcedure/i,
]

/**
 * Patterns that indicate authenticated middleware
 */
const AUTH_MIDDLEWARE_PATTERNS = [
  /protectedProcedure/i,
  /authenticatedProcedure/i,
  /requireAuth/i,
  /isAuthenticated/i,
  /privateProcedure/i,
  /authedProcedure/i,
  /userProcedure/i,
  /loggedInProcedure/i,
]

/**
 * Patterns that indicate public/unprotected procedures
 */
const PUBLIC_MIDDLEWARE_PATTERNS = [
  /publicProcedure/i,
  /guestProcedure/i,
  /unauthenticatedProcedure/i,
  /openProcedure/i,
]

/**
 * Detect tRPC usage in files
 */
function detectTRPCUsage(content: string): boolean {
  const trpcPatterns = [
    /@trpc\/server/i,
    /@trpc\/client/i,
    /@trpc\/react-query/i,
    /@trpc\/next/i,
    /createTRPCRouter/i,
    /initTRPC/i,
    /trpc\.router/i,
    /t\.router\s*\(/i,
    /t\.procedure/i,
  ]
  return trpcPatterns.some(p => p.test(content))
}

/**
 * Extract middleware definitions from a file
 */
function extractMiddlewares(content: string, filePath: string): TRPCMiddleware[] {
  const middlewares: TRPCMiddleware[] = []
  const lines = content.split('\n')

  // Look for procedure definitions with middleware
  const procedureDefPatterns = [
    /export\s+const\s+(\w+Procedure)\s*=\s*t\.procedure\.use/i,
    /const\s+(\w+Procedure)\s*=\s*t\.procedure\.use/i,
    /(\w+Procedure)\s*=\s*publicProcedure\.use/i,
    /export\s+const\s+(\w+Procedure)\s*=/i,
  ]

  for (let i = 0; i < lines.length; i++) {
    const line = lines[i]
    
    for (const pattern of procedureDefPatterns) {
      const match = line.match(pattern)
      if (match) {
        const name = match[1]
        let type: 'admin' | 'authenticated' | 'public' | 'unknown' = 'unknown'

        // Determine type based on name and context
        if (ADMIN_MIDDLEWARE_PATTERNS.some(p => p.test(name) || p.test(line))) {
          type = 'admin'
        } else if (AUTH_MIDDLEWARE_PATTERNS.some(p => p.test(name) || p.test(line))) {
          type = 'authenticated'
        } else if (PUBLIC_MIDDLEWARE_PATTERNS.some(p => p.test(name) || p.test(line))) {
          type = 'public'
        }

        middlewares.push({
          name,
          type,
          definedIn: filePath,
          lineNumber: i + 1,
        })
      }
    }
  }

  return middlewares
}

/**
 * Extract router definitions from a file
 */
function extractRouters(content: string, filePath: string, middlewares: Map<string, TRPCMiddleware>): TRPCRouter[] {
  const routers: TRPCRouter[] = []
  const lines = content.split('\n')

  // Pattern to find router definitions
  const routerDefPatterns = [
    /export\s+const\s+(\w+Router)\s*=\s*(?:createTRPCRouter|t\.router)\s*\(\s*\{/i,
    /const\s+(\w+Router)\s*=\s*(?:createTRPCRouter|t\.router)\s*\(\s*\{/i,
    /(\w+):\s*(?:createTRPCRouter|t\.router)\s*\(\s*\{/i,
  ]

  for (let i = 0; i < lines.length; i++) {
    const line = lines[i]
    
    for (const pattern of routerDefPatterns) {
      const match = line.match(pattern)
      if (match) {
        const name = match[1].replace(/Router$/i, '').toLowerCase()
        
        // Look ahead to find procedures in this router
        const procedures: string[] = []
        let isProtected = false
        let defaultMiddleware: string | undefined

        // Check if router name suggests admin protection
        if (/admin/i.test(name)) {
          isProtected = true
          defaultMiddleware = 'adminProcedure'
        }

        // Scan the router body for procedures
        let braceDepth = 0
        let inRouter = false
        for (let j = i; j < Math.min(i + 100, lines.length); j++) {
          const routerLine = lines[j]
          
          if (routerLine.includes('{')) {
            braceDepth += (routerLine.match(/\{/g) || []).length
            inRouter = true
          }
          if (routerLine.includes('}')) {
            braceDepth -= (routerLine.match(/\}/g) || []).length
          }

          if (inRouter && braceDepth === 0) break

          // Look for procedure definitions
          const procMatch = routerLine.match(/(\w+)\s*:\s*(\w+Procedure)\./i)
          if (procMatch) {
            procedures.push(procMatch[1])
            const middlewareName = procMatch[2]
            const middleware = middlewares.get(middlewareName)
            if (middleware && (middleware.type === 'admin' || middleware.type === 'authenticated')) {
              isProtected = true
            }
          }

          // Check for admin/protected procedure usage
          if (ADMIN_MIDDLEWARE_PATTERNS.some(p => p.test(routerLine))) {
            isProtected = true
          } else if (AUTH_MIDDLEWARE_PATTERNS.some(p => p.test(routerLine))) {
            isProtected = true
          }
        }

        routers.push({
          name,
          file: filePath,
          procedures,
          defaultMiddleware,
          isProtected,
        })
      }
    }
  }

  return routers
}

/**
 * Analyze tRPC routers across all files
 */
export function analyzeTRPCRouters(files: ScanFile[]): TRPCRouterContext {
  const context: TRPCRouterContext = {
    routers: new Map(),
    procedures: new Map(),
    middlewares: new Map(),
    hasTRPC: false,
  }

  // First pass: detect if tRPC is used
  for (const file of files) {
    if (detectTRPCUsage(file.content)) {
      context.hasTRPC = true
      break
    }
  }

  if (!context.hasTRPC) {
    return context
  }

  // Second pass: extract middlewares
  for (const file of files) {
    const middlewares = extractMiddlewares(file.content, file.path)
    for (const middleware of middlewares) {
      context.middlewares.set(middleware.name, middleware)
    }
  }

  // Third pass: extract routers
  for (const file of files) {
    const routers = extractRouters(file.content, file.path, context.middlewares)
    for (const router of routers) {
      context.routers.set(router.name, router)
      
      // Add procedures to the procedures map
      for (const procName of router.procedures) {
        context.procedures.set(`${router.name}.${procName}`, {
          name: procName,
          router: router.name,
          hasAuthMiddleware: router.isProtected,
          middlewareType: router.isProtected ? 'authenticated' : 'public',
          definedIn: router.file,
        })
      }
    }
  }

  return context
}

/**
 * Check if a tRPC procedure is protected by middleware
 */
export function isProcedureProtected(
  context: TRPCRouterContext,
  routerName: string,
  procedureName?: string
): boolean {
  // Check if the router itself is protected
  const router = context.routers.get(routerName.toLowerCase())
  if (router?.isProtected) {
    return true
  }

  // Check specific procedure if provided
  if (procedureName) {
    const procedure = context.procedures.get(`${routerName.toLowerCase()}.${procedureName}`)
    if (procedure?.hasAuthMiddleware) {
      return true
    }
  }

  // Check if router name suggests admin protection
  if (/admin/i.test(routerName)) {
    return true
  }

  return false
}

/**
 * Parse a tRPC client call to extract router and procedure names
 * e.g., trpc.admin.getUsers.useQuery() -> { router: 'admin', procedure: 'getUsers' }
 */
export function parseTRPCCall(lineContent: string): { router: string; procedure: string } | null {
  // Pattern: trpc.routerName.procedureName
  const patterns = [
    /trpc\.(\w+)\.(\w+)\./i,
    /api\.(\w+)\.(\w+)\./i,
    /client\.(\w+)\.(\w+)\./i,
  ]

  for (const pattern of patterns) {
    const match = lineContent.match(pattern)
    if (match) {
      return {
        router: match[1],
        procedure: match[2],
      }
    }
  }

  return null
}

/**
 * Check if a line contains a tRPC client call
 */
export function isTRPCClientCall(lineContent: string): boolean {
  const patterns = [
    /trpc\.\w+\.\w+\.(useQuery|useMutation|useInfiniteQuery)/i,
    /api\.\w+\.\w+\.(useQuery|useMutation|useInfiniteQuery)/i,
    /trpc\.\w+\.\w+\.query\(/i,
    /trpc\.\w+\.\w+\.mutate\(/i,
  ]
  return patterns.some(p => p.test(lineContent))
}

/**
 * Get a summary of tRPC context for logging
 */
export function getTRPCSummary(context: TRPCRouterContext): string {
  if (!context.hasTRPC) {
    return 'No tRPC detected'
  }

  const parts: string[] = []
  parts.push(`tRPC detected: ${context.routers.size} routers, ${context.procedures.size} procedures`)

  const protectedRouters = Array.from(context.routers.values()).filter(r => r.isProtected)
  if (protectedRouters.length > 0) {
    parts.push(`Protected routers: ${protectedRouters.map(r => r.name).join(', ')}`)
  }

  return parts.join('; ')
}
