import { execSync } from 'node:child_process'
import { existsSync, readdirSync, readFileSync } from 'node:fs'
import { join } from 'node:path'
import { describe, expect, it } from 'vitest'

const SRC = join(__dirname)
const PKG_ROOT = join(__dirname, '..')

// @stripe/* client SDKs (@stripe/stripe-js, @stripe/react-stripe-js) are allowed.
// This matches the bare server Node SDK module specifier `stripe` in any import position.
const SERVER_STRIPE_RE =
  /(?:from|import|require\()\s*['"]stripe['"]|import\s*\(\s*['"]stripe['"]/

const BILLING_RUNTIME_RE = /(?:from|import|require\()\s*['"]@platform-modules\/billing['"]/

function srcFiles(dir: string): string[] {
  return readdirSync(dir, { withFileTypes: true }).flatMap((e) => {
    const p = join(dir, e.name)
    if (e.isDirectory()) return srcFiles(p)
    return /\.(ts|tsx)$/.test(e.name) && !/\.test\.(ts|tsx)$/.test(e.name) ? [p] : []
  })
}

function distOffenders(text: string, label: string, opts?: { allowBillingTypeRef?: boolean }): string[] {
  const offenders: string[] = []
  if (SERVER_STRIPE_RE.test(text)) offenders.push(`${label} → server 'stripe' SDK`)
  if (!opts?.allowBillingTypeRef && (BILLING_RUNTIME_RE.test(text) || text.includes('@platform-modules/billing'))) {
    offenders.push(`${label} → @platform-modules/billing runtime (must be type-only / externalized)`)
  }
  if (/\b(secretKey|webhookSecret)\b/.test(text)) offenders.push(`${label} → secret symbol`)
  return offenders
}

describe('no server-only / secret surface leaks into the browser sibling (§5/§7 guard)', () => {
  it('SERVER_STRIPE_RE flags dynamic import("stripe") but not @stripe/stripe-js', () => {
    expect(SERVER_STRIPE_RE.test("const m = await import('stripe')")).toBe(true)
    expect(SERVER_STRIPE_RE.test('await import("@stripe/stripe-js")')).toBe(false)
  })

  it('no src file value-imports the server stripe SDK or billing runtime, and no secret symbol appears', () => {
    const offenders: string[] = []
    for (const f of srcFiles(SRC)) {
      const text = readFileSync(f, 'utf8')
      const lines = text.split('\n')
      lines.forEach((line, i) => {
        const at = `${f}:${i + 1}`
        if (SERVER_STRIPE_RE.test(line)) offenders.push(`${at} → server 'stripe' SDK`)
        if (BILLING_RUNTIME_RE.test(line) && !/import\s+type\b/.test(line)) {
          offenders.push(`${at} → runtime import of @platform-modules/billing (must be import type)`)
        }
        if (/\b(secretKey|webhookSecret)\b/.test(line)) offenders.push(`${at} → secret symbol`)
      })
    }
    expect(offenders, offenders.join('\n')).toEqual([])
  })

  it('built dist bundle carries no server stripe SDK, billing runtime, or secret symbols', () => {
    const distJs = join(PKG_ROOT, 'dist', 'index.js')
    const distDts = join(PKG_ROOT, 'dist', 'index.d.ts')
    if (!existsSync(distJs)) {
      execSync('pnpm build', { cwd: PKG_ROOT, stdio: 'inherit' })
    }

    const js = readFileSync(distJs, 'utf8')
    const dts = existsSync(distDts) ? readFileSync(distDts, 'utf8') : ''

    // Security-critical: catches a bundler regression that inlines a forbidden module.
    const offenders = [
      ...distOffenders(js, 'dist/index.js'),
      // .d.ts may reference billing for ChargeResult type linkage (import type only — no runtime).
      ...distOffenders(dts, 'dist/index.d.ts', { allowBillingTypeRef: true }),
    ]

    expect(offenders, offenders.join('\n')).toEqual([])

    // Client Stripe SDKs remain external import specifiers (allowed).
    expect(js).toMatch(/@stripe\/stripe-js/)
    expect(js).toMatch(/@stripe\/react-stripe-js/)
  })
})