/**
 * Tests for Module Graph — buildModuleGraph, resolveImportedName, circular detection
 */

import { buildModuleGraph, resolveImportedName } from '../../model/module-graph'
import type { ScanFile } from '../../shared/types'

function makeFile(path: string, content: string): ScanFile {
  return { path, content, language: 'typescript' }
}

describe('buildModuleGraph', () => {
  it('builds graph with forward and reverse edges', () => {
    const files = [
      makeFile('src/routes/users.ts', `
import { getUser } from '../lib/db'
export async function GET(req) { return getUser(req.params.id) }
      `),
      makeFile('src/lib/db.ts', `
export function getUser(id: string) { return db.query('SELECT * FROM users WHERE id = ' + id) }
      `),
    ]

    const graph = buildModuleGraph(files)
    expect(graph.nodes.size).toBe(2)

    const routeNode = graph.nodes.get('src/routes/users.ts')
    expect(routeNode).toBeDefined()
    expect(routeNode!.dependsOn.has('src/lib/db.ts')).toBe(true)

    const dbNode = graph.nodes.get('src/lib/db.ts')
    expect(dbNode).toBeDefined()
    expect(dbNode!.importedBy.has('src/routes/users.ts')).toBe(true)
  })

  it('counts edges correctly', () => {
    const files = [
      makeFile('src/a.ts', `import { b } from './b'\nimport { c } from './c'`),
      makeFile('src/b.ts', `export const b = 1`),
      makeFile('src/c.ts', `export const c = 2`),
    ]

    const graph = buildModuleGraph(files)
    expect(graph.stats.totalEdges).toBe(2)
    expect(graph.stats.totalFiles).toBe(3)
  })

  it('detects circular imports', () => {
    const files = [
      makeFile('src/a.ts', `import { b } from './b'\nexport const a = 1`),
      makeFile('src/b.ts', `import { a } from './a'\nexport const b = 2`),
    ]

    const graph = buildModuleGraph(files)
    expect(graph.circularImports.size).toBeGreaterThan(0)
    expect(graph.stats.circularImportCount).toBeGreaterThan(0)
  })

  it('handles no circular imports', () => {
    const files = [
      makeFile('src/a.ts', `import { b } from './b'`),
      makeFile('src/b.ts', `export const b = 1`),
    ]

    const graph = buildModuleGraph(files)
    expect(graph.circularImports.size).toBe(0)
  })

  it('skips non-code files', () => {
    const files = [
      makeFile('src/a.ts', `export const a = 1`),
      makeFile('README.md', `# Hello`),
      makeFile('.env', `SECRET=123`),
    ]

    const graph = buildModuleGraph(files)
    expect(graph.nodes.size).toBe(1)
  })

  it('handles node_modules imports (not resolved)', () => {
    const files = [
      makeFile('src/app.ts', `import express from 'express'\nimport { db } from './db'`),
      makeFile('src/db.ts', `export const db = {}`),
    ]

    const graph = buildModuleGraph(files)
    const appNode = graph.nodes.get('src/app.ts')!
    // express import should not create a resolved edge
    expect(appNode.dependsOn.size).toBe(1)
    expect(appNode.dependsOn.has('src/db.ts')).toBe(true)
  })

  it('extracts exports for nodes', () => {
    const files = [
      makeFile('src/db.ts', `
export function getUser(id) { }
export function createUser(data) { }
export const DB_NAME = 'mydb'
      `),
    ]

    const graph = buildModuleGraph(files)
    const node = graph.nodes.get('src/db.ts')!
    expect(node.exports.length).toBe(3)
    expect(node.exports.map(e => e.name)).toEqual(expect.arrayContaining(['getUser', 'createUser', 'DB_NAME']))
  })

  it('includes duration in stats', () => {
    const files = [makeFile('src/a.ts', `export const a = 1`)]
    const graph = buildModuleGraph(files)
    expect(graph.stats.duration).toBeGreaterThanOrEqual(0)
  })
})

describe('resolveImportedName', () => {
  it('resolves a directly exported name', () => {
    const files = [
      makeFile('src/routes.ts', `import { getUser } from './db'`),
      makeFile('src/db.ts', `export function getUser(id) { }`),
    ]

    const graph = buildModuleGraph(files)
    const resolved = resolveImportedName(graph, 'src/routes.ts', 'getUser')
    expect(resolved).not.toBeNull()
    expect(resolved!.filePath).toBe('src/db.ts')
    expect(resolved!.exportName).toBe('getUser')
  })

  it('returns null for unknown import', () => {
    const files = [
      makeFile('src/routes.ts', `import { getUser } from './db'`),
      makeFile('src/db.ts', `export function getUser(id) { }`),
    ]

    const graph = buildModuleGraph(files)
    const resolved = resolveImportedName(graph, 'src/routes.ts', 'nonExistent')
    expect(resolved).toBeNull()
  })

  it('returns null for unknown file', () => {
    const files = [makeFile('src/a.ts', `export const a = 1`)]
    const graph = buildModuleGraph(files)
    const resolved = resolveImportedName(graph, 'src/nonexistent.ts', 'a')
    expect(resolved).toBeNull()
  })

  it('resolves through one-hop re-export', () => {
    const files = [
      makeFile('src/app.ts', `import { query } from './index'`),
      makeFile('src/index.ts', `export { query } from './db'`),
      makeFile('src/db.ts', `export function query(sql) { }`),
    ]

    const graph = buildModuleGraph(files)
    const resolved = resolveImportedName(graph, 'src/app.ts', 'query')
    expect(resolved).not.toBeNull()
    expect(resolved!.filePath).toBe('src/db.ts')
  })
})
