import subprocess,os,re,json,sys
ROOT='/home/madsc/work/insuniverse-temp/.worktrees/admin-rbac/insuniverse-server'
OUT='/home/madsc/work/insuniverse-temp/.worktrees/admin-rbac/.local/analysis-impl/neg2'
OLD='58d078eb'
M='apps/api/src/modules/'
MAP=M+'auth/permission/route-permission-map.ts'
VIEW=M+'analyze/analyze-admin-view.controller.ts'
B2C=M+'b2c/analyze/b2c-analyze-admin-view.controller.ts'
HEAD_DEL_ROW="  ['GET /admin-view/b2c-analyze/:bodId/basic', 'b2c-analyze-admin.read'],\n"
port=subprocess.check_output(['docker','port','analysis-impl-mysql','3306'],text=True).strip().rsplit(':',1)[1]
SPECS=[M+'analyze/analysis-access.mysql.spec.ts',M+'auth/access-contract/access-contract.spec.ts',M+'auth/analysis-target.guard.spec.ts','apps/api/src/helpers/admin-viewer.helper.spec.ts']
def rep(a,b): return ('rep',a,b)
CASES=[
 ('맵 한 행 빼기',{MAP:rep(HEAD_DEL_ROW,'')},[]),
 ('상속 끊기(extends 제거)',{VIEW:rep('export class AdminViewAnalyzeController extends AdminAnalyzeController {}','export class AdminViewAnalyzeController {}')},[]),
 ('상속 끊기(자식이 클래스 가드를 덮음)',{VIEW:rep("import { AdminAnalyzeController } from './analyze-admin.controller';\n","import { UseGuards } from '@nestjs/common';\nimport { AdminJwtAuthGuard } from '../auth/admin/jwt.guard';\nimport { RolesGuard } from '../auth/admin/roles.guard';\nimport { AdminAnalyzeController } from './analyze-admin.controller';\n")},['@UseGuards']),
 ('상속 끊기(자식이 역할을 넓힘)',{VIEW:rep("import { AdminAnalyzeController } from './analyze-admin.controller';\n","import { Roles } from '../auth/admin/roles.decorator';\nimport { AdminAnalyzeController } from './analyze-admin.controller';\n")},['@Roles']),
 ('토큰 헤더로 통과 시도: 공개 경로 선택 가드의 토큰 분기 복원',None,[M+'auth/admin/admin-optional.guard.ts','apps/api/src/helpers/admin-viewer.helper.ts',M+'push/push.module.ts']),
 ('토큰 헤더로 통과 시도: 대상 가드의 헤더 검증 복원',None,[M+'auth/analysis-target.guard.ts']),
 ('토큰 헤더로 통과 시도: B2C 열람 컨트롤러를 토큰 가드로 복원',None,[B2C,M+'b2c/analyze/b2c-analyze.module.ts',M+'admin-view-token/admin-view-token.guard.ts',M+'admin-view-token/admin-view-token.module.ts',M+'analyze/analyze.repository.ts']),
 ("B2C 열람 컨트롤러에서 @Roles('admin') 제거",{B2C:rep("@Roles('admin')\n",'')},[]),
]
only=sys.argv[1:]
results=[]
env=dict(os.environ,ANALYZE_SALES_SCOPE_MYSQL_PORT=port)
for name,edits,restore in CASES:
    if only and not any(o in name for o in only): continue
    assert subprocess.run(['git','diff','--quiet'],cwd=ROOT).returncode==0,'tree dirty'
    touched=[];created=[]
    try:
        if edits:
            for f,(k,a,b) in edits.items():
                p=os.path.join(ROOT,f); s=open(p,encoding='utf8').read()
                assert a in s,(name,f,a[:40])
                s=s.replace(a,b,1)
                if restore==['@UseGuards']: s=s.replace("@Controller('admin-view/analyze')","@UseGuards(AdminJwtAuthGuard, RolesGuard)\n@Controller('admin-view/analyze')")
                if restore==['@Roles']: s=s.replace("@Controller('admin-view/analyze')","@Roles('admin', 'sales', 'corp', 'org-manager')\n@Controller('admin-view/analyze')")
                open(p,'w',encoding='utf8').write(s); touched.append(f)
        else:
            for f in restore:
                p=os.path.join(ROOT,f)
                existed=os.path.exists(p)
                data=subprocess.check_output(['git','show',f'{OLD}:{f}'],cwd=ROOT)
                open(p,'wb').write(data)
                (touched if existed else created).append(f)
        log=os.path.join(OUT,re.sub(r'\W+','_',name)+'.log')
        r=subprocess.run(['systemd-run','--user','--scope','-q','-p','MemoryMax=3G','-p','MemorySwapMax=0','npx','jest','--maxWorkers=2','--forceExit','--runTestsByPath',*SPECS],cwd=ROOT,env=env,stdout=open(log,'w'),stderr=subprocess.STDOUT,timeout=500)
        t=open(log,encoding='utf8').read()
        fails=sorted(set(re.findall(r'^\s+● (.+)$',t,re.M)))
        wrong=re.findall(r'^\s+\+\s+"([^"]{0,240})',t,re.M)[:6]
        total=re.search(r'^Tests:.*$',t,re.M)
        results.append({'case':name,'rc':r.returncode,'tests':total.group(0) if total else None,'fails':fails,'sample':wrong})
        print('##',name,'rc=',r.returncode,total.group(0) if total else None)
        print('\n'.join('  '+f[:140] for f in fails)); print('\n'.join('    e.g. '+w[:220] for w in wrong[:3]),flush=True)
    finally:
        for f in touched: subprocess.run(['git','checkout','--',f],cwd=ROOT)
        for f in created: os.remove(os.path.join(ROOT,f))
    assert subprocess.run(['git','diff','--quiet'],cwd=ROOT).returncode==0
    assert not subprocess.check_output(['git','status','--short'],cwd=ROOT,text=True).strip()
json.dump(results,open(os.path.join(OUT,'neg-results.json'),'w'),ensure_ascii=False,indent=1)
print('NEG-DONE',flush=True)
