/**
 * Tests for source discovery — identifying user-input sources in code.
 */

import { discoverSources } from '../../model/source-discovery'

describe('discoverSources', () => {
  describe('Express/Node.js patterns', () => {
    it('detects req.body with direct assignment', () => {
      const code = `
const data = req.body
`
      const sources = discoverSources(code, 'src/routes/api.ts')
      expect(sources).toHaveLength(1)
      expect(sources[0].variable).toBe('data')
      expect(sources[0].sourceType).toBe('http_body')
      expect(sources[0].confidence).toBe('high')
    })

    it('detects req.query', () => {
      const code = `const q = req.query`
      const sources = discoverSources(code, 'src/routes/search.ts')
      expect(sources.some(s => s.sourceType === 'http_query')).toBe(true)
    })

    it('detects req.params', () => {
      const code = `const { id } = req.params`
      const sources = discoverSources(code, 'src/routes/user.ts')
      expect(sources.some(s => s.sourceType === 'http_params')).toBe(true)
      expect(sources.some(s => s.variable === 'id')).toBe(true)
    })

    it('detects req.headers', () => {
      const code = `const auth = req.headers`
      const sources = discoverSources(code, 'src/middleware.ts')
      expect(sources.some(s => s.sourceType === 'http_headers')).toBe(true)
    })

    it('detects req.cookies', () => {
      const code = `const session = req.cookies`
      const sources = discoverSources(code, 'src/auth.ts')
      expect(sources.some(s => s.sourceType === 'http_headers')).toBe(true)
    })
  })

  describe('Destructuring patterns', () => {
    it('extracts multiple variables from destructured req.body', () => {
      const code = `const { username, password, email } = req.body`
      const sources = discoverSources(code, 'src/routes/register.ts')
      const vars = sources.map(s => s.variable)
      expect(vars).toContain('username')
      expect(vars).toContain('password')
      expect(vars).toContain('email')
    })

    it('handles renamed destructuring', () => {
      const code = `const { name: userName } = req.body`
      const sources = discoverSources(code, 'src/routes/user.ts')
      expect(sources.some(s => s.variable === 'userName')).toBe(true)
    })

    it('handles array destructuring', () => {
      const code = `const [ first, second ] = req.body.items`
      const sources = discoverSources(code, 'src/routes/api.ts')
      expect(sources.some(s => s.variable === 'first')).toBe(true)
      expect(sources.some(s => s.variable === 'second')).toBe(true)
    })
  })

  describe('Next.js patterns', () => {
    it('detects searchParams', () => {
      const code = `const query = searchParams`
      const sources = discoverSources(code, 'src/app/page.tsx')
      expect(sources.some(s => s.sourceType === 'http_query')).toBe(true)
      expect(sources[0].confidence).toBe('medium')
    })

    it('detects formData', () => {
      const code = `const data = formData`
      const sources = discoverSources(code, 'src/app/actions.ts')
      expect(sources.some(s => s.sourceType === 'form_data')).toBe(true)
    })
  })

  describe('Browser patterns', () => {
    it('detects document.location', () => {
      const code = `const url = document.location`
      const sources = discoverSources(code, 'src/client/app.ts')
      expect(sources.some(s => s.sourceType === 'url_search_params')).toBe(true)
    })

    it('detects URLSearchParams', () => {
      const code = `const params = new URLSearchParams(window.location.search)`
      const sources = discoverSources(code, 'src/client/search.ts')
      expect(sources.length).toBeGreaterThanOrEqual(1)
    })

    it('detects document.cookie', () => {
      const code = `const cookies = document.cookie`
      const sources = discoverSources(code, 'src/client/auth.ts')
      expect(sources.some(s => s.sourceType === 'http_headers')).toBe(true)
    })
  })

  describe('Python patterns', () => {
    it('detects Flask request.args', () => {
      const code = `query = request.args`
      const sources = discoverSources(code, 'app/views.py')
      expect(sources.some(s => s.sourceType === 'http_query')).toBe(true)
      expect(sources[0].confidence).toBe('high')
    })

    it('detects Django request.POST', () => {
      const code = `data = request.POST`
      const sources = discoverSources(code, 'app/views.py')
      expect(sources.some(s => s.sourceType === 'form_data')).toBe(true)
    })
  })

  describe('AWS Lambda patterns', () => {
    it('detects event.body', () => {
      const code = `const body = event.body`
      const sources = discoverSources(code, 'src/lambda/handler.ts')
      expect(sources.some(s => s.sourceType === 'http_body')).toBe(true)
      expect(sources[0].confidence).toBe('high')
    })

    it('detects event.queryStringParameters', () => {
      const code = `const params = event.queryStringParameters`
      const sources = discoverSources(code, 'src/lambda/handler.ts')
      expect(sources.some(s => s.sourceType === 'http_query')).toBe(true)
    })
  })

  describe('CLI patterns', () => {
    it('detects process.argv with low confidence', () => {
      const code = `const args = process.argv`
      const sources = discoverSources(code, 'src/cli/main.ts')
      expect(sources.some(s => s.sourceType === 'cli_args')).toBe(true)
      expect(sources[0].confidence).toBe('low')
    })
  })

  describe('WebSocket patterns', () => {
    it('detects ws.on message', () => {
      const code = `ws.on('message', (data) => { const input = data })`
      const sources = discoverSources(code, 'src/ws/handler.ts')
      expect(sources.some(s => s.sourceType === 'websocket_message')).toBe(true)
    })
  })

  describe('Edge cases', () => {
    it('skips comment lines', () => {
      const code = `
// const data = req.body
/* const query = req.query */
`
      const sources = discoverSources(code, 'src/routes/api.ts')
      expect(sources).toHaveLength(0)
    })

    it('handles inline usage without assignment', () => {
      const code = `db.query(req.query.id)`
      const sources = discoverSources(code, 'src/routes/api.ts')
      expect(sources.length).toBeGreaterThanOrEqual(1)
      expect(sources[0].sourceType).toBe('http_query')
    })

    it('returns empty for files with no user input', () => {
      const code = `
const config = { port: 3000, host: 'localhost' }
export default config
`
      const sources = discoverSources(code, 'src/config.ts')
      expect(sources).toHaveLength(0)
    })

    it('deduplicates sources on the same line', () => {
      const code = `const data = req.body`
      const sources = discoverSources(code, 'src/routes/api.ts')
      // Should only produce one source, not duplicates
      const uniqueKeys = new Set(sources.map(s => `${s.line}:${s.variable}:${s.sourceType}`))
      expect(uniqueKeys.size).toBe(sources.length)
    })
  })
})
