import { expect, test } from "bun:test"
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"
import path from "node:path"

// The engine is the reusable, content-agnostic layer: it must import nothing from
// game (content) or app (browser glue). game -> engine and app -> game -> engine are
// fine; the reverse is the violation. This freezes the Stage-3 seam so a stray import
// can't quietly re-couple the layers.
function tsFiles(dir: string): string[] {
  const out: string[] = []
  for (const name of readdirSync(dir)) {
    const p = path.join(dir, name)
    if (statSync(p).isDirectory()) {
      out.push(...tsFiles(p))
    } else if (p.endsWith(".ts")) {
      out.push(p)
    }
  }
  return out
}

function repositoryRoot(): string {
  const parent = new URL("..", import.meta.url).pathname
  return existsSync(path.join(parent, "app/renderer.ts")) ? parent : path.join(parent, "..")
}

function importsLayer(source: string, layers: string): boolean {
  return new RegExp(`(?:from\\s+|import\\s*(?:\\(\\s*)?)["'][^"']*/(?:${layers})/`).test(source)
}

test("engine imports nothing from game or app", () => {
  const engineDir = path.join(repositoryRoot(), "engine")
  const offenders = tsFiles(engineDir).filter((file) => importsLayer(readFileSync(file, "utf8"), "game|app"))
  expect(offenders).toEqual([])
})

test("game imports nothing from app", () => {
  const gameDir = path.join(repositoryRoot(), "game")
  const offenders = tsFiles(gameDir).filter((file) => importsLayer(readFileSync(file, "utf8"), "app"))
  expect(offenders).toEqual([])
})

test("render driver and worker know no game content", () => {
  const root = repositoryRoot()
  const files = [path.join(root, "app/renderer.ts"), path.join(root, "app/render-worker.ts")]
  const offenders = files.filter((file) => importsLayer(readFileSync(file, "utf8"), "game"))
  expect(offenders).toEqual([])
})

test("game exports no closed content-kind registries", () => {
  const gameDir = path.join(repositoryRoot(), "game")
  const offenders = tsFiles(gameDir).filter((file) => {
    const source = readFileSync(file, "utf8")
    return (
      /\b(?:export\s+)?(?:type|enum)\s+\w+Kind\b/.test(source) ||
      /\b(?:const|let|var)\s+\w*_(?:KINDS|REGISTRY)\b/.test(source)
    )
  })
  expect(offenders).toEqual([])
})
