/**
 * OAuth Flow Detector
 * Detects OAuth state generation and validation across multiple files
 * to prevent false positives when state is implemented correctly but split across files.
 */

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

export interface OAuthFlowContext {
  /** Whether state generation is detected in any file */
  hasStateGeneration: boolean
  /** File where state is generated */
  stateGenerationFile?: string
  /** Line number where state is generated */
  stateGenerationLine?: number
  /** Whether state validation is detected in any file */
  hasStateValidation: boolean
  /** File where state is validated */
  stateValidationFile?: string
  /** Line number where state is validated */
  stateValidationLine?: number
  /** Whether PKCE code_verifier is used */
  hasCodeVerifier: boolean
  /** File where code_verifier is generated */
  codeVerifierFile?: string
  /** Detected OAuth flow type */
  flowType: 'authorization_code' | 'client_credentials' | 'implicit' | 'pkce' | 'unknown'
  /** OAuth providers detected */
  providers: string[]
}

/**
 * Patterns that indicate OAuth state generation
 */
const STATE_GENERATION_PATTERNS = [
  /generateState\s*\(/i,
  /crypto\.randomBytes.*state/i,
  /state\s*=\s*.*random/i,
  /setCookie.*oauth.*state/i,
  /cookies?\(\s*\)\.set\s*\([^)]*state/i,
  /state\s*=\s*generateRandomString/i,
  /state\s*=\s*nanoid\s*\(/i,
  /state\s*=\s*uuid/i,
  /state\s*=\s*crypto\.randomUUID/i,
  /createState\s*\(/i,
  /oauth.*state.*=.*crypto/i,
  /\.setState\s*\(/i,
  /stateParam\s*=\s*/i,
  /generateOAuthState/i,
]

/**
 * Patterns that indicate OAuth state validation
 */
const STATE_VALIDATION_PATTERNS = [
  /state\s*!==\s*storedState/i,
  /storedState.*!==.*state/i,
  /validateState\s*\(/i,
  /getCookie.*oauth.*state/i,
  /cookies?\(\s*\)\.get\s*\([^)]*state/i,
  /verifyState\s*\(/i,
  /checkState\s*\(/i,
  /state\s*===\s*savedState/i,
  /savedState\s*===\s*state/i,
  /if\s*\(\s*!?\s*state\s*\)/i,
  /state\s*!==\s*.*\.get\s*\(/i,
  /\.getState\s*\(/i,
  /compareState\s*\(/i,
]

/**
 * Patterns that indicate PKCE code_verifier usage
 */
const CODE_VERIFIER_PATTERNS = [
  /code_verifier/i,
  /codeVerifier/i,
  /generateCodeVerifier/i,
  /createCodeVerifier/i,
  /pkce/i,
  /code_challenge/i,
  /codeChallenge/i,
]

/**
 * Patterns that indicate client_credentials flow (no state needed)
 */
const CLIENT_CREDENTIALS_PATTERNS = [
  /grant_type.*client_credentials/i,
  /client_credentials.*grant/i,
  /\.clientCredentials\s*\(/i,
  /getClientCredentialsToken/i,
  /machine-to-machine/i,
  /m2m.*auth/i,
]

/**
 * Known OAuth provider patterns
 */
const OAUTH_PROVIDER_PATTERNS: Record<string, RegExp[]> = {
  google: [/googleapis\.com\/oauth/i, /accounts\.google\.com/i, /google.*oauth/i],
  github: [/github\.com\/login\/oauth/i, /api\.github\.com.*oauth/i],
  microsoft: [/login\.microsoftonline\.com/i, /microsoft.*oauth/i, /azure.*oauth/i],
  facebook: [/facebook\.com\/.*oauth/i, /graph\.facebook\.com/i],
  twitter: [/twitter\.com\/oauth/i, /api\.twitter\.com.*oauth/i],
  apple: [/appleid\.apple\.com/i, /apple.*oauth/i],
  auth0: [/auth0\.com/i, /\.auth0\./i],
  okta: [/okta\.com/i, /\.okta\./i],
  clerk: [/clerk\.dev/i, /clerk\.com/i, /@clerk\//i],
  nextauth: [/next-auth/i, /nextauth/i, /authjs/i],
  lucia: [/lucia-auth/i, /lucia\.ts/i],
  arctic: [/arctic/i, /oslo\/oauth/i],
}

/**
 * Detect OAuth flow patterns across multiple files
 */
export function detectOAuthFlow(files: ScanFile[]): OAuthFlowContext {
  const context: OAuthFlowContext = {
    hasStateGeneration: false,
    hasStateValidation: false,
    hasCodeVerifier: false,
    flowType: 'unknown',
    providers: [],
  }

  const detectedProviders = new Set<string>()

  for (const file of files) {
    const content = file.content
    const lines = content.split('\n')

    // Check for state generation
    if (!context.hasStateGeneration) {
      for (let i = 0; i < lines.length; i++) {
        const line = lines[i]
        if (STATE_GENERATION_PATTERNS.some(p => p.test(line))) {
          context.hasStateGeneration = true
          context.stateGenerationFile = file.path
          context.stateGenerationLine = i + 1
          break
        }
      }
    }

    // Check for state validation
    if (!context.hasStateValidation) {
      for (let i = 0; i < lines.length; i++) {
        const line = lines[i]
        if (STATE_VALIDATION_PATTERNS.some(p => p.test(line))) {
          context.hasStateValidation = true
          context.stateValidationFile = file.path
          context.stateValidationLine = i + 1
          break
        }
      }
    }

    // Check for PKCE
    if (!context.hasCodeVerifier) {
      if (CODE_VERIFIER_PATTERNS.some(p => p.test(content))) {
        context.hasCodeVerifier = true
        context.codeVerifierFile = file.path
      }
    }

    // Check for client_credentials flow
    if (context.flowType === 'unknown') {
      if (CLIENT_CREDENTIALS_PATTERNS.some(p => p.test(content))) {
        context.flowType = 'client_credentials'
      }
    }

    // Detect OAuth providers
    for (const [provider, patterns] of Object.entries(OAUTH_PROVIDER_PATTERNS)) {
      if (patterns.some(p => p.test(content))) {
        detectedProviders.add(provider)
      }
    }
  }

  // Determine flow type
  if (context.flowType === 'unknown') {
    if (context.hasCodeVerifier) {
      context.flowType = 'pkce'
    } else if (context.hasStateGeneration || context.hasStateValidation) {
      context.flowType = 'authorization_code'
    }
  }

  context.providers = Array.from(detectedProviders)

  return context
}

/**
 * Check if OAuth state is properly implemented across files
 */
export function isOAuthStateImplemented(context: OAuthFlowContext): boolean {
  // client_credentials flow doesn't need state
  if (context.flowType === 'client_credentials') {
    return true
  }

  // PKCE flow with code_verifier is secure even without traditional state
  if (context.hasCodeVerifier) {
    return true
  }

  // Both generation and validation must be present
  return context.hasStateGeneration && context.hasStateValidation
}

/**
 * Get a summary of the OAuth flow for logging/context
 */
export function getOAuthFlowSummary(context: OAuthFlowContext): string {
  const parts: string[] = []

  if (context.flowType !== 'unknown') {
    parts.push(`Flow: ${context.flowType}`)
  }

  if (context.providers.length > 0) {
    parts.push(`Providers: ${context.providers.join(', ')}`)
  }

  if (context.hasStateGeneration) {
    parts.push(`State generated in: ${context.stateGenerationFile}`)
  }

  if (context.hasStateValidation) {
    parts.push(`State validated in: ${context.stateValidationFile}`)
  }

  if (context.hasCodeVerifier) {
    parts.push(`PKCE enabled (${context.codeVerifierFile})`)
  }

  if (isOAuthStateImplemented(context)) {
    parts.push('✓ OAuth state properly implemented')
  } else if (context.hasStateGeneration || context.hasStateValidation) {
    parts.push('⚠ Partial OAuth state implementation detected')
  }

  return parts.join('; ')
}
