const test = require('node:test')
const assert = require('node:assert')
const fs = require('fs')
const path = require('path')
const os = require('os')
const { applyLibraryDepsRewrite } = require('../src/lib/library-deps-hint')
const { applyDepsFileUpdates } = require('../src/commands/template-from-jsx-shared')

test('applyLibraryDepsRewrite skips cmsDependencies registry for import type from npm', () => {
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'bild-lib-deps-type-import-'))
  const component = path.join(dir, 'Widget.tsx')
  fs.writeFileSync(
    component,
    `import type { ComponentType } from 'react'\nimport { Rocket } from 'lucide-react'\nexport function Widget() { return <Rocket /> }\n`,
    'utf8',
  )
  const depsFile = path.join(dir, 'cmsDependencies.ts')
  fs.writeFileSync(
    depsFile,
    `import * as React from 'react'\nexport const cmsDependencies = {\n  react: { module: React },\n}\n`,
    'utf8',
  )

  const { report } = applyLibraryDepsRewrite(component, {
    depsFile,
    depsImportAlias: '@/cmsDependencies',
  })

  assert.ok(!report.depsPatch.recordEntries.some((e) => e.spec === 'react'))
  assert.ok(report.depsPatch.recordEntries.some((e) => e.spec === 'lucide-react'))
})

test('applyLibraryDepsRewrite skips types-only subpaths like swiper/types (no namespace import)', () => {
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'bild-lib-deps-swiper-types-'))
  const component = path.join(dir, 'Slide.tsx')
  fs.writeFileSync(
    component,
    `import type { SwiperOptions } from 'swiper/types'\nimport { Swiper, SwiperSlide } from 'swiper/react'\nexport function Slide() { return <Swiper><SwiperSlide /></Swiper> }\n`,
    'utf8',
  )
  const depsFile = path.join(dir, 'cmsDependencies.ts')
  fs.writeFileSync(
    depsFile,
    `import * as React from 'react'\nexport const cmsDependencies = {\n  react: { module: React },\n}\n`,
    'utf8',
  )

  const { report } = applyLibraryDepsRewrite(component, {
    depsFile,
    depsImportAlias: '@/cmsDependencies',
  })

  assert.ok(!report.depsPatch.recordEntries.some((e) => e.spec === 'swiper/types'))
  assert.ok(!report.depsPatch.namespaceImports.some((i) => i.spec === 'swiper/types'))
  assert.ok(report.depsPatch.recordEntries.some((e) => e.spec === 'swiper/react'))
})

test('applyLibraryDepsRewrite leaves external npm imports as direct imports', async () => {
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'bild-lib-deps-'))
  const component = path.join(dir, 'Card.tsx')
  fs.writeFileSync(
    component,
    `import { useState } from 'react'\nimport { Rocket } from 'lucide-react'\nexport function Card() { return <Rocket /> }\n`,
    'utf8',
  )
  const depsFile = path.join(dir, 'cmsDependencies.ts')
  fs.writeFileSync(
    depsFile,
    `import * as React from 'react'\nexport const cmsDependencies = {\n  react: { module: React },\n}\n`,
    'utf8',
  )

  const { code, applied, depsFilePath, report } = applyLibraryDepsRewrite(component, {
    depsFile,
    depsImportAlias: '@/cmsDependencies',
  })

  assert.strictEqual(applied, false)
  assert.strictEqual(depsFilePath, depsFile)
  assert.ok(report.depsPatch.recordEntries.some((e) => e.spec === 'lucide-react'))
  assert.ok(report.depsPatch.namespaceImports.some((i) => i.spec === 'lucide-react' && i.namespaceId === 'LucideReact'))
  assert.match(code, /import \{ Rocket \} from ['"]lucide-react['"]/)
  assert.ok(!/cmsDependencies\[\s*['"]lucide-react['"]\s*\]/.test(code))

  await applyDepsFileUpdates(depsFile, report, 'test')
  const depsText = fs.readFileSync(depsFile, 'utf8')
  assert.match(depsText, /import \* as LucideReact from ['"]lucide-react['"]/)
  assert.match(depsText, /['"]lucide-react['"]:\s*\{\s*module:\s*LucideReact/)
})

test('applyLibraryDepsRewrite codegen succeeds when recoverable parse leaves TSTypeCastExpression', () => {
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'bild-lib-deps-cast-'))
  const component = path.join(dir, 'Cast.tsx')
  fs.writeFileSync(
    component,
    `import { Rocket } from 'lucide-react'
export function Cast() {
  const _ = (foo: string);
  return <Rocket />
}
`,
    'utf8',
  )
  const depsFile = path.join(dir, 'cmsDependencies.ts')
  fs.writeFileSync(
    depsFile,
    `import * as React from 'react'
export const cmsDependencies = {
  react: { module: React },
}
`,
    'utf8',
  )

  const { code, applied } = applyLibraryDepsRewrite(component, {
    depsFile,
    depsImportAlias: '@/cmsDependencies',
  })

  assert.strictEqual(applied, true)
  assert.match(code, /\bas string\b/)
  assert.match(code, /import \{ Rocket \} from ['"]lucide-react['"]/)
  assert.ok(!/cmsDependencies\[\s*['"]lucide-react['"]\s*\]/.test(code))
})

test('@/ path alias rewrites to the Components barrel when sitePath is set', () => {
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'bild-lib-deps-alias-path-'))
  fs.writeFileSync(path.join(dir, 'package.json'), '{"name":"alias-test"}\n')
  fs.mkdirSync(path.join(dir, 'src', 'utils'), { recursive: true })
  fs.writeFileSync(
    path.join(dir, 'src/utils/cn.ts'),
    'export function cn(..._c: string[]) { return "" }\n',
  )
  const component = path.join(dir, 'src/Hero.tsx')
  fs.writeFileSync(
    component,
    `import { cn } from '@/utils/cn'\nexport function Hero() { return <div className={cn("a")} /> }\n`,
    'utf8',
  )
  const depsFile = path.join(dir, 'cmsDependencies.ts')
  fs.writeFileSync(
    depsFile,
    `import * as React from 'react'\nexport const cmsDependencies = {\n  react: { module: React },\n}\n`,
    'utf8',
  )

  const { code, applied, report } = applyLibraryDepsRewrite(component, {
    depsFile,
    sitePath: dir,
    depsImportAlias: '@/cmsDependencies',
  })

  assert.strictEqual(applied, true)
  assert.ok(!report.depsPatch.recordEntries.some((e) => e.spec === '@/utils/cn'))
  assert.ok(!report.depsPatch.namespaceImports.some((i) => i.spec === '@/utils/cn'))
  assert.match(code, /import \{ cn \} from ['"]@\/app\/components\/Components['"]/)
  assert.ok(!/cmsDependencies\[\s*['"]@\/utils\/cn['"]\s*\]/.test(code))
})

test('applyLibraryDepsRewrite does not treat cmsDependencies import alias as an external package', () => {
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'bild-lib-deps-alias-'))
  const component = path.join(dir, 'Banner.tsx')
  fs.writeFileSync(
    component,
    `import { cmsDependencies } from '@/cmsDependencies'\nimport { Rocket } from 'lucide-react'\nexport function Banner() { return <Rocket /> }\n`,
    'utf8',
  )
  const depsFile = path.join(dir, 'cmsDependencies.ts')
  fs.writeFileSync(
    depsFile,
    `import * as React from 'react'\nexport const cmsDependencies = {\n  react: { module: React },\n}\n`,
    'utf8',
  )

  const { code, applied, report } = applyLibraryDepsRewrite(component, {
    depsFile,
    depsImportAlias: '@/cmsDependencies',
  })

  assert.strictEqual(applied, false)
  assert.ok(!report.moved.includes('@/cmsDependencies'))
  assert.ok(!report.depsPatch.namespaceImports.some((i) => i.spec === '@/cmsDependencies'))
  assert.ok(!report.depsPatch.recordEntries.some((e) => e.spec === '@/cmsDependencies'))
  assert.ok(report.depsPatch.recordEntries.some((e) => e.spec === 'lucide-react'))
  assert.match(code, /import \{ cmsDependencies \} from ['"]@\/cmsDependencies['"]/)
  assert.match(code, /import \{ Rocket \} from ['"]lucide-react['"]/)
  assert.ok(!/cmsDependencies\[\s*['"]lucide-react['"]\s*\]/.test(code))
})

test('with sitePath, local @/ import uses Components barrel; cms file does not get export * from the barrel', async () => {
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'bild-barrel-'))
  fs.writeFileSync(path.join(dir, 'package.json'), '{"name":"barrel-test"}\n')
  fs.mkdirSync(path.join(dir, 'src', 'utils'), { recursive: true })
  fs.writeFileSync(
    path.join(dir, 'src/utils/cn.ts'),
    'export function cn(..._c: string[]) { return "" }\n',
  )
  const component = path.join(dir, 'src', 'Page.tsx')
  fs.writeFileSync(
    component,
    `import { cn } from '@/utils/cn'\nexport function Page() { return <div className={cn("a")} /> }\n`,
    'utf8',
  )
  const depsFile = path.join(dir, 'src', 'cmsDependencies.ts')
  fs.writeFileSync(
    depsFile,
    `import * as React from 'react'
export const cmsDependencies = { react: { module: React } }
`,
    'utf8',
  )

  const { code, report } = applyLibraryDepsRewrite(component, {
    depsFile,
    sitePath: dir,
    depsImportAlias: '@/cmsDependencies',
  })

  assert.match(code, /import \{ cn \} from ['"]@\/app\/components\/Components['"]/)
  assert.ok(report.useBarrelReexports)
  assert.ok(
    report.barrelReexportLines.some((l) => l.includes('cn') && l.includes(`'@/utils/cn'`)),
  )
  assert.ok(!String(report.appendedSource || '').includes('function cn'))
  const { applyDepsFileUpdates } = require('../src/commands/template-from-jsx-shared')
  await applyDepsFileUpdates(depsFile, report, 'test')
  const barrel = fs.readFileSync(
    path.join(dir, 'src', 'app', 'components', 'Components.tsx'),
    'utf8',
  )
  assert.match(barrel, /export \{\s*cn\s*\}\s*from\s*['"]@\/utils\/cn['"]/)
  const cms = fs.readFileSync(depsFile, 'utf8')
  assert.ok(!/export \* from ['"]@\/app\/components\/Components['"]/.test(cms))
})
