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

const read=p=>JSON.parse(fs.readFileSync(p,'utf8'))
const src=read('contracts/configuration/kbx.configuration.json')
const providers=read('contracts/providers/kbx.providers.json')
const manifest=read('generated/configuration-manifest.json')
const errors=[]

const keys=new Set(),envs=new Set()
for(const s of src.settings??[]){
  if(keys.has(s.key))errors.push(`duplicate configuration key ${s.key}`);keys.add(s.key)
  if(envs.has(s.env))errors.push(`duplicate environment variable ${s.env}`);envs.add(s.env)
  if(s.secret && s.default!==undefined)errors.push(`secret setting must not define a default: ${s.key}`)
  if(s.secret && !(s.allowedSources??[]).every(x=>['environment','secret-store','user-secrets'].includes(x)))errors.push(`secret setting has unsafe source: ${s.key}`)
  if(s.type==='enum' && !(s.allowedValues?.length))errors.push(`enum setting missing allowedValues: ${s.key}`)
  if((s.minimum!==undefined||s.maximum!==undefined)&&s.type!=='integer')errors.push(`numeric bounds only supported for integer in v22: ${s.key}`)
}
for(const e of src.environments??[]){
  if(!['Development','Test','Staging','Production'].includes(e.id))errors.push(`unsupported environment profile ${e.id}`)
}
const production=src.environments.find(x=>x.id==='Production')
if(!production||production.migrationStrategy!=='predeploy')errors.push('Production must use predeploy migration strategy')
if(!production?.requireHttps)errors.push('Production must require HTTPS')
if(production?.providerEndpointOverrideAllowed)errors.push('Production provider endpoint overrides must be forbidden')
const test=src.environments.find(x=>x.id==='Test')
if(!test||test.providerNetwork!=='forbidden')errors.push('Test environment must forbid live provider network access')

const providerCredentialKeys=[]
for(const p of providers.providers??[]){
  const policy=p.kbxPolicy??{}
  for(const prop of ['credentialKey','appKeyCredential','appSecretCredential','environmentKey'])if(policy[prop])providerCredentialKeys.push(policy[prop])
}
for(const key of providerCredentialKeys)if(!keys.has(key))errors.push(`provider configuration key missing from configuration catalog: ${key}`)

if(manifest.settingCount!==src.settings.length||manifest.environmentCount!==src.environments.length)errors.push('generated configuration manifest count drift')
const ts=fs.readFileSync('packages/kbx-contracts/src/generated/configurationCatalog.ts','utf8')
const cs=fs.readFileSync('backend/Shared/Configuration/Generated/KbxConfigurationCatalog.g.cs','utf8')
if(!ts.includes(manifest.sourceSha256)||!cs.includes(manifest.sourceSha256))errors.push('configuration generated source SHA parity missing')

const envTemplate=fs.readFileSync('deploy/kbx/.env.kbx.example','utf8')
for(const s of src.settings){
  if(!envTemplate.includes(`${s.env}=`))errors.push(`env template missing ${s.env}`)
  if(s.secret){
    const line=envTemplate.split(/\r?\n/).find(x=>x.startsWith(`${s.env}=`))??''
    const raw=line.slice(s.env.length+1).split(' # ')[0]
    if(raw.trim())errors.push(`generated env template contains a secret value: ${s.env}`)
  }
}

function walk(dir){
  const out=[]
  if(!fs.existsSync(dir))return out
  for(const e of fs.readdirSync(dir,{withFileTypes:true})){
    const p=path.join(dir,e.name)
    if(e.isDirectory())out.push(...walk(p));else out.push(p)
  }
  return out
}
// Direct environment/config access outside the configuration boundary becomes hidden configuration debt.
for(const file of walk('backend').filter(x=>x.endsWith('.cs')&&!x.includes('/Shared/Configuration/'))){
  const text=fs.readFileSync(file,'utf8')
  if(/Environment\.GetEnvironmentVariable\s*\(/.test(text))errors.push(`direct environment read outside configuration boundary: ${file}`)
  if(/IConfiguration\s*\[/.test(text))errors.push(`direct IConfiguration index usage outside configuration boundary: ${file}`)
}

// Heuristic committed-secret guard for known provider/alerting key names.
for(const file of walk('.').filter(x=>/\.(json|ya?ml|md|env|txt)$/i.test(x)&&!x.includes('/.git/')&&!x.includes('governance/baselines/'))){
  if(file==='contracts/configuration/kbx.configuration.json'||file==='contracts/providers/kbx.providers.json'||file==='deploy/kbx/.env.kbx.example')continue
  const text=fs.readFileSync(file,'utf8')
  if(/(?:AUTH_KEY|crtfc_key|AppSecret|BotToken)\s*[=:]\s*["']?[A-Za-z0-9_\-]{16,}/i.test(text))errors.push(`possible committed secret material: ${file}`)
}

for(const f of ['backend/Shared/Configuration/KbxConfigurationStartupValidator.cs','backend/Shared/Configuration/KbxConfigurationFingerprint.cs','deploy/kbx/environment-matrix.generated.json','docs/configuration-reference.generated.md'])if(!fs.existsSync(f))errors.push(`missing v22 configuration artifact ${f}`)

if(errors.length){console.error('configuration governance FAIL');for(const e of errors)console.error(`- ${e}`);process.exit(1)}
console.log(`configuration governance PASS: settings=${src.settings.length}, environments=${src.environments.length}, provider credentials covered=${providerCredentialKeys.length}`)
