import fs from 'node:fs'
import path from 'node:path'

const args = Object.fromEntries(process.argv.slice(2).flatMap((value, index, all) => {
  if (!value.startsWith('--')) return []
  const key = value.slice(2)
  const next = all[index + 1]
  return [[key, next && !next.startsWith('--') ? next : 'true']]
}))

const required = ['module', 'area', 'number', 'type', 'name', 'path', 'permission']
for (const key of required) if (!args[key]) throw new Error(`missing --${key}`)

const moduleName = args.module.toUpperCase()
const area = args.area.toUpperCase()
const number = String(args.number).padStart(3, '0')
const type = args.type
const title = args.name
const screenId = `${moduleName}-${area}-${number}`
const allowedModules = new Set(['OMS', 'ERP', 'WMS', 'COMMON'])
const allowedTypes = new Set(['list','master','transaction','fast-entry','master-detail','queue','reconcile','import','wms-mobile'])
if (!allowedModules.has(moduleName)) throw new Error(`invalid module: ${moduleName}`)
if (!allowedTypes.has(type)) throw new Error(`invalid type: ${type}`)
if (!/^\d{3}$/.test(number)) throw new Error('number must resolve to 3 digits')

const root = process.env.KBX_ROOT ? path.resolve(process.env.KBX_ROOT) : process.cwd()
const recipeContract=JSON.parse(fs.readFileSync(path.join(process.cwd(),'contracts/screens/kbx.screen-recipes.json'),'utf8'))
const recipe=recipeContract.recipes.find(item=>item.type===type)
if(!recipe)throw new Error(`screen recipe missing for type ${type}`)
const needsWrite=recipe.defaultCommands.some(command=>command.permissionKind==='write')
if(needsWrite&&!args['write-permission'])throw new Error(`--write-permission is required for ${type} screens; mutating commands must never be scaffolded without an explicit permission`)
if (path.isAbsolute(args.path) || args.path.split(/[\\/]/).includes('..')) throw new Error('--path must be a safe relative module path')
const modulesRoot = path.join(root, 'apps/web/src/modules')
const target = path.resolve(modulesRoot, args.path)
if (!target.startsWith(path.resolve(modulesRoot) + path.sep)) throw new Error('--path escapes modules root')
if (fs.existsSync(target) && fs.readdirSync(target).length > 0) throw new Error(`target already exists and is not empty: ${target}`)
fs.mkdirSync(target, { recursive: true })

const token = `${moduleName.toLowerCase()}${area.charAt(0).toUpperCase()}${area.slice(1).toLowerCase()}${number}`
const screenVar = `${token}Screen`
const pageName = `${moduleName.charAt(0)}${moduleName.slice(1).toLowerCase()}${area.charAt(0)}${area.slice(1).toLowerCase()}${number}Page`
const definitionFile = `${token}.definition.ts`
const pageFile = `${pageName}.vue`
const q=value=>`'${String(value).replaceAll('\\','\\\\').replaceAll("'","\\'")}'`
const commandLines=recipe.defaultCommands.map(command=>{
  const permission=command.permissionKind==='write'?args['write-permission']:(command.permissionKind==='execute'?args.permission:null)
  const fields=[`id:${q(command.id)}`,`label:${q(command.label)}`,`group:${q(command.group)}`]
  if(command.shortcut)fields.push(`shortcut:${q(command.shortcut)}`)
  if(command.variant)fields.push(`variant:${q(command.variant)}`)
  if(permission)fields.push(`permission:${q(permission)}`)
  return `    { ${fields.join(', ')} },`
})

fs.writeFileSync(path.join(target, definitionFile), `import { defineKbxScreen } from '@kbx/ui'\n\nexport const ${screenVar} = defineKbxScreen({\n  id: '${screenId}',\n  version: '1.0.0',\n  module: '${moduleName}',\n  type: '${type}',\n  templateCode: '${recipe.code}',\n  title: '${title}',\n  helpKey: '${screenId}',\n  permissions: ['${args.permission}'],\n  commands: [\n${commandLines.join('\n')}\n  ],\n  telemetry: { enabled: true },\n})\n`)

const blueprint = {
  list:{component:'KbxListPage',imports:['KbxSearchPanel','KbxDataGrid','KbxSummaryBar'],setup:`const search=reactive<Record<string,unknown>>({})\nconst searchFields:KbxSearchField[]=[]\nconst rows:Record<string,unknown>[]=[]\nconst columns:KbxGridColumn<Record<string,unknown>>[]=[]\nconst summaryItems:KbxSummaryItem[]=[]`,slots:`    <template #search><KbxSearchPanel v-model="search" :fields="searchFields" @search="execute('search')" /></template>\n    <template #content><KbxDataGrid :rows="rows" :columns="columns" row-key="id" /></template>\n    <template #summary><KbxSummaryBar :items="summaryItems" /></template>`},
  master:{component:'KbxMasterPage',imports:['KbxDataGrid','KbxFormGrid','KbxFormSection'],setup:`const rows:Record<string,unknown>[]=[]\nconst columns:KbxGridColumn<Record<string,unknown>>[]=[]`,slots:`    <template #list><KbxDataGrid :rows="rows" :columns="columns" row-key="id" /></template>\n    <template #detail><KbxFormSection title="기본정보"><KbxFormGrid><div>TODO: 업무 필드</div></KbxFormGrid></KbxFormSection></template>`},
  transaction:{component:'KbxTransactionPage',imports:['KbxDataGrid','KbxFormGrid','KbxFormSection','KbxSummaryBar'],setup:`const rows:Record<string,unknown>[]=[]\nconst columns:KbxGridColumn<Record<string,unknown>>[]=[]\nconst summaryItems:KbxSummaryItem[]=[]`,slots:`    <template #header><KbxFormSection title="기본정보"><KbxFormGrid><div>TODO: Header 업무 필드</div></KbxFormGrid></KbxFormSection></template>\n    <template #detail><KbxDataGrid :rows="rows" :columns="columns" row-key="clientId" editable /></template>\n    <template #summary><KbxSummaryBar :items="summaryItems" align="end" /></template>`},
  'fast-entry':{component:'KbxFastEntryPage',imports:['KbxDataGrid','KbxValidationSummary','KbxSummaryBar'],setup:`const rows:Record<string,unknown>[]=[]\nconst columns:KbxGridColumn<Record<string,unknown>>[]=[]\nconst errors:KbxValidationError[]=[]\nconst summaryItems:KbxSummaryItem[]=[]`,slots:`    <template #guide><span>Enter 다음 셀 · F2 조회 · Ctrl+V 붙여넣기</span></template>\n    <template #content><KbxDataGrid :rows="rows" :columns="columns" row-key="clientId" editable /></template>\n    <template #validation><KbxValidationSummary :errors="errors" /></template>\n    <template #summary><KbxSummaryBar :items="summaryItems" /></template>`},
  'master-detail':{component:'KbxMasterDetailPage',imports:['KbxSearchPanel','KbxDataGrid'],setup:`const search=reactive<Record<string,unknown>>({})\nconst searchFields:KbxSearchField[]=[]\nconst rows:Record<string,unknown>[]=[]\nconst columns:KbxGridColumn<Record<string,unknown>>[]=[]`,slots:`    <template #search><KbxSearchPanel v-model="search" :fields="searchFields" @search="execute('search')" /></template>\n    <template #master><KbxDataGrid :rows="rows" :columns="columns" row-key="id" /></template>\n    <template #detail><KbxDataGrid :rows="[]" :columns="columns" row-key="id" /></template>`},
  queue:{component:'KbxQueuePage',imports:['KbxDataGrid','KbxExceptionSummary','KbxSummaryBar'],setup:`const rows:Record<string,unknown>[]=[]\nconst columns:KbxGridColumn<Record<string,unknown>>[]=[]\nconst counters:KbxWorkQueueCounter[]=[]\nconst summaryItems:KbxSummaryItem[]=[]`,slots:`    <template #summary><KbxSummaryBar :items="summaryItems" /></template>\n    <template #exceptions><KbxExceptionSummary :counters="counters" /></template>\n    <template #content><KbxDataGrid :rows="rows" :columns="columns" row-key="id" /></template>`},
  reconcile:{component:'KbxReconcilePage',imports:['KbxSearchPanel','KbxDataGrid'],setup:`const search=reactive<Record<string,unknown>>({})\nconst searchFields:KbxSearchField[]=[]\nconst rows:Record<string,unknown>[]=[]\nconst columns:KbxGridColumn<Record<string,unknown>>[]=[]`,slots:`    <template #search><KbxSearchPanel v-model="search" :fields="searchFields" @search="execute('search')" /></template>\n    <template #content><KbxDataGrid :rows="rows" :columns="columns" row-key="id" /></template>\n    <template #resolution><span>TODO: 권한이 명시된 해결 Action</span></template>`},
  import:{component:'KbxImportPage',imports:['KbxProgressSteps','KbxExcelImport'],setup:`const steps=[{key:'file',label:'파일'},{key:'mapping',label:'매핑'},{key:'validation',label:'검증'},{key:'commit',label:'반영'}]`,slots:`    <template #steps><KbxProgressSteps :steps="steps" active-key="file" /></template>\n    <KbxExcelImport />\n    <template #result><span>TODO: 반영 결과</span></template>`},
  'wms-mobile':{component:'KbxWmsMobilePage',imports:['KbxBarcodeCapture','KbxNetworkIndicator','KbxWmsActionButton'],extraProps:' :online="true"',setup:'',slots:`    <KbxBarcodeCapture />\n    <template #actions><KbxWmsActionButton label="작업 실행" /></template>`},
}[type]
const imports=[blueprint.component,...blueprint.imports]
const needsReactive=blueprint.setup.includes('reactive<')
const contractTypes=[]
for(const typeName of ['KbxSearchField','KbxGridColumn','KbxSummaryItem','KbxValidationError','KbxWorkQueueCounter'])if(blueprint.setup.includes(typeName))contractTypes.push(typeName)
const typeImport=contractTypes.length?`\nimport type { ${contractTypes.join(', ')} } from '@kbx/contracts'`:''
const vueImport=needsReactive?"\nimport { reactive } from 'vue'":''
fs.writeFileSync(path.join(target, pageFile), `<script setup lang="ts">\nimport { ${imports.join(', ')} } from '@kbx/ui'${typeImport}${vueImport}\nimport { ${screenVar} } from './${definitionFile.replace(/\.ts$/, '')}'\n\n${blueprint.setup}\nfunction execute(commandId:string){ void commandId /* TODO: connect business-specific orchestration only. */ }\n</script>\n\n<template>\n  <${blueprint.component} :screen="${screenVar}"${blueprint.extraProps??''} @command="execute">\n${blueprint.slots}\n  </${blueprint.component}>\n</template>\n`)

fs.writeFileSync(path.join(target, 'routes.ts'), `export const route = {\n  path: '/${args.path.replaceAll('\\\\','/').replaceAll('\\','/')}',\n  name: '${screenId}',\n  component: () => import('./${pageFile}'),\n  meta: { screenId: '${screenId}', permission: '${args.permission}' },\n}\n`)

const testPlanVar=`${token}TestPlan`
fs.writeFileSync(path.join(target, `${token}.test-plan.ts`), `import type { KbxGeneratedScreenTestPlan } from '@kbx/contracts'\n\nexport const ${testPlanVar} = {\n  screenId: '${screenId}',\n  templateCode: '${recipe.code}',\n  canonicalScenarioIds: ${JSON.stringify(recipe.canonicalScenarioIds)},\n  requiredChecks: ${JSON.stringify(recipe.testProfile.requiredChecks)},\n  requiredEvidence: ${JSON.stringify(recipe.testProfile.requiredEvidence)},\n} as const satisfies KbxGeneratedScreenTestPlan\n`)

const lines=[`# ${screenId} ${title}`,'',`Generated by KBX scaffolder using ${recipe.code} ${type} recipe.`,'','## Recipe policies','',...recipe.requiredPolicies.map(x=>`- Data/process: \`${x}\``),...recipe.recoveryPolicies.map(x=>`- Recovery: \`${x}\``),...recipe.securityPolicies.map(x=>`- Security: \`${x}\``),'','## Canonical regression scenarios','',...recipe.canonicalScenarioIds.map(x=>`- \`${x}\``),'','## Must complete before merge','','- Replace TODO business fields/data only; keep the canonical KBX template/component composition.','- Bind server query/command contracts; do not move business truth into the client.','- Keep Loading / Empty / Error / Refreshing and permission behavior explicit.','- Use the generated `*.test-plan.ts` as the minimum verification contract, then add screen-specific Vitest/Playwright evidence.','- Run `node scripts/validate-kbx.mjs` before merge.','']
fs.writeFileSync(path.join(target,'README.md'),lines.join('\n'))

const helpFile = path.join(root, 'apps/web/src/help/helpRegistry.ts')
let help = fs.readFileSync(helpFile, 'utf8')
if (!help.includes(`'${screenId}'`)) {
  const insert = `  '${screenId}': {\n    key: '${screenId}', title: '${title}', purpose: 'TODO: 이 화면의 업무 목적을 한 문장으로 작성하세요.',\n    steps: ['TODO: 사용자가 실제로 수행하는 첫 번째 업무 단계를 작성하세요.'],\n  },\n`
  const pos = help.lastIndexOf('}')
  help = help.slice(0, pos) + insert + help.slice(pos)
  fs.writeFileSync(helpFile, help)
}

console.log(`created ${screenId} at ${path.relative(root, target)}`)
console.log(`recipe ${recipe.code} ${type} -> ${recipe.templateComponent}`)
console.log(`canonical scenarios: ${recipe.canonicalScenarioIds.join(', ')}`)
console.log('run: node scripts/generate-app-screen-registry.mjs && node scripts/validate-kbx.mjs')
