import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { spawnSync } from 'node:child_process'

const root = fs.mkdtempSync(path.join(os.tmpdir(), 'kbx-scaffold-'))
fs.mkdirSync(path.join(root, 'apps/web/src/help'), { recursive: true })
fs.writeFileSync(path.join(root, 'apps/web/src/help/helpRegistry.ts'), "import type { KbxHelpContent } from '@kbx/contracts'\nexport const helpRegistry: Record<string, KbxHelpContent> = {\n}\n")
const recipeContract=JSON.parse(fs.readFileSync('contracts/screens/kbx.screen-recipes.json','utf8'))
const recipeByType=new Map(recipeContract.recipes.map(recipe=>[recipe.type,recipe]))

const cases=[
  ['list','KbxListPage',['KbxSearchPanel','KbxDataGrid','KbxSummaryBar']],
  ['master','KbxMasterPage',['KbxDataGrid','KbxFormGrid','KbxFormSection']],
  ['transaction','KbxTransactionPage',['KbxDataGrid','KbxFormGrid','KbxSummaryBar']],
  ['fast-entry','KbxFastEntryPage',['KbxDataGrid','KbxValidationSummary','KbxSummaryBar']],
  ['master-detail','KbxMasterDetailPage',['KbxSearchPanel','KbxDataGrid']],
  ['queue','KbxQueuePage',['KbxExceptionSummary','KbxDataGrid','KbxSummaryBar']],
  ['reconcile','KbxReconcilePage',['KbxSearchPanel','KbxDataGrid']],
  ['import','KbxImportPage',['KbxProgressSteps','KbxExcelImport']],
  ['wms-mobile','KbxWmsMobilePage',['KbxBarcodeCapture','KbxWmsActionButton']],
]
for(const [index,[type,component,needles]] of cases.entries()){
  const number=String(901+index)
  const modulePath=`erp/scaffold/${type}`
  const args=[
    'scripts/create-kbx-screen.mjs','--module','ERP','--area','SCF','--number',number,'--type',type,
    '--name',`스캐폴드 ${type}`,'--path',modulePath,'--permission','erp.item.read',
  ]
  if(['master','transaction','fast-entry'].includes(type))args.push('--write-permission','erp.item.write')
  const result = spawnSync(process.execPath,args,{ cwd: process.cwd(), env: { ...process.env, KBX_ROOT: root }, encoding: 'utf8' })
  if (result.status !== 0) { console.error(result.stdout, result.stderr); process.exit(result.status ?? 1) }
  const target=path.join(root,'apps/web/src/modules',modulePath)
  const definition=fs.readdirSync(target).find(file=>file.endsWith('.definition.ts'))
  const page=fs.readdirSync(target).find(file=>file.endsWith('Page.vue'))
  if(!definition||!page)throw new Error(`scaffolder ${type} missing definition/page`)
  const definitionText=fs.readFileSync(path.join(target,definition),'utf8')
  const text=fs.readFileSync(path.join(target,page),'utf8')
  const readme=fs.readFileSync(path.join(target,'README.md'),'utf8')
  const testPlanFile=fs.readdirSync(target).find(file=>file.endsWith('.test-plan.ts'))
  if(!testPlanFile)throw new Error(`scaffolder ${type} missing generated recipe test plan`)
  const testPlan=fs.readFileSync(path.join(target,testPlanFile),'utf8')
  const recipe=recipeByType.get(type)
  if(!recipe)throw new Error(`recipe missing for ${type}`)
  if(!definitionText.includes(`templateCode: '${recipe.code}'`))throw new Error(`scaffolder ${type} missing ${recipe.code}`)
  if(!text.includes(component))throw new Error(`scaffolder ${type} must use ${component}`)
  for(const needle of needles)if(!text.includes(needle))throw new Error(`scaffolder ${type} missing ${needle}`)
  for(const scenario of recipe.canonicalScenarioIds){
    if(!readme.includes(scenario))throw new Error(`scaffolder ${type} README missing canonical scenario ${scenario}`)
    if(!testPlan.includes(scenario))throw new Error(`scaffolder ${type} test plan missing canonical scenario ${scenario}`)
  }
  for(const check of recipe.testProfile.requiredChecks)if(!testPlan.includes(check))throw new Error(`scaffolder ${type} test plan missing required check ${check}`)
  for(const evidence of recipe.testProfile.requiredEvidence)if(!testPlan.includes(evidence))throw new Error(`scaffolder ${type} test plan missing evidence ${evidence}`)
  if(/primevue\//.test(text)||/ag-grid-vue3/.test(text))throw new Error(`scaffolder ${type} leaks underlying UI libraries`)
  if(['master','transaction','fast-entry'].includes(type)&&!/permission:'erp\.item\.write'/.test(definitionText))throw new Error(`scaffolder ${type} mutating commands must carry explicit write permission`)
}

const unsafeRoot=fs.mkdtempSync(path.join(os.tmpdir(),'kbx-scaffold-deny-'))
fs.mkdirSync(path.join(unsafeRoot,'apps/web/src/help'),{recursive:true})
fs.writeFileSync(path.join(unsafeRoot,'apps/web/src/help/helpRegistry.ts'),"export const helpRegistry = {}\n")
const denied=spawnSync(process.execPath,['scripts/create-kbx-screen.mjs','--module','ERP','--area','DEN','--number','999','--type','transaction','--name','권한 없는 거래','--path','erp/scaffold/deny','--permission','erp.item.read'],{cwd:process.cwd(),env:{...process.env,KBX_ROOT:unsafeRoot},encoding:'utf8'})
if(denied.status===0||!`${denied.stderr}${denied.stdout}`.includes('--write-permission is required'))throw new Error('scaffolder must fail closed when a mutating recipe has no explicit write permission')
fs.rmSync(unsafeRoot,{recursive:true,force:true})

const help = fs.readFileSync(path.join(root, 'apps/web/src/help/helpRegistry.ts'),'utf8')
if((help.match(/^  'ERP-SCF-9\d\d':/gm)??[]).length!==9)throw new Error('scaffolder did not add all help registry stubs')
console.log('KBX scaffolder contract PASS (T01-T09 recipe-driven composition, generated test plans, canonical scenarios, templateCode, and fail-closed write permissions)')
fs.rmSync(root, { recursive: true, force: true })
