import fs from 'node:fs';import {expect,test}from '@playwright/test';import {adminEnv,root,recovery,keys,preset,selected,openPreset,save,screen,fresh,go,settled,refresh,persist,record,blocked,type Role,type Check}from './permission-support';
for(const role of ['CO','BO'] as Role[])test(`[${role}] 실제 전월 매출집계 및 법인 조회 범위`,async({browser},info)=>{
 test.setTimeout(900000);test.skip(process.env.QA_ALLOW_PERMISSION_MUTATION!=='1','BLOCKED: mutation guard missing');const file=`${root}/matrix/${role}.json`;const rows:Check[]=JSON.parse(fs.readFileSync(file,'utf8'));const original=JSON.parse(fs.readFileSync(`${recovery}/${role}-baseline.json`,'utf8'));const c=await browser.newContext({storageState:adminEnv().storageState,viewport:{width:1600,height:1000}});const a=await c.newPage();let p=await fresh(browser,role);const put=(id:string,ok:boolean,detail:string,shots:string[])=>{for(let i=rows.length-1;i>=0;i--)if(rows[i].id===id&&rows[i].status==='BLOCKED')rows.splice(i,1);record(rows,role,'previous month scope',id,ok,detail,shots);persist(role,rows)};
 const toggle=async(st:boolean)=>{await openPreset(a,role);await save(a,role,{'스토어 거래조회':true,'스토어 정산조회':st,'매출집계 조회':true},'전월 조회 범위·매출집계');await refresh(p);await settled(p);await screen(p,info,`${role}-profit-${st?'on':'off'}-refresh`);await p.context().close();p=await fresh(browser,role);};
 try{await go(p,'/');await toggle(true);
  if(role==='CO'){
   await go(p,'/transactions');await p.getByRole('button',{name:'전월',exact:true}).click();const fullResponse=p.waitForResponse(r=>new URL(r.url()).pathname==='/api/v1/store/transactions');await p.getByRole('button',{name:'조회',exact:true}).click();const all=(await(await fullResponse).json()).payload?.items?.content??[];await settled(p);const allShot=await screen(p,info,'CO-scope-all-previous-month');
   const owner=JSON.parse(fs.readFileSync(`${recovery}/CO-user-detail.json`,'utf8')).payload.departmentId;const branches=(await(await p.context().request.get(`https://api-dev.biscuitpay.com/api/v1/store/data/branch-offices?departmentId=${owner}`)).json()).payload??[];let candidate:any;
   for(const branch of branches){const vendors=(await(await p.context().request.get(`https://api-dev.biscuitpay.com/api/v1/store/data/vendors?departmentId=${branch.departmentId}`)).json()).payload??[];const vendor=vendors.find((v:any)=>v.vendorId&&all.some((x:any)=>x.vendorName===v.vendorName));if(vendor){candidate={branch,vendor};break;}}
   if(candidate){const branchLabel=p.locator('label').filter({has:p.getByText('영업점',{exact:true})}).first(),vendorLabel=p.locator('label').filter({has:p.getByText('가맹점',{exact:true})}).first();await branchLabel.getByRole('button').click();await p.getByRole('option',{name:candidate.branch.departmentName,exact:true}).click();await expect(vendorLabel.getByRole('button')).toBeEnabled({timeout:60000});await vendorLabel.getByRole('button').click();const totalResponse=p.waitForResponse(r=>new URL(r.url()).pathname==='/api/v1/store/transactions/total'&&new URL(r.url()).searchParams.get('departmentId')===candidate.vendor.vendorId,{timeout:60000});await p.getByRole('option',{name:candidate.vendor.vendorName,exact:true}).click();const response=await totalResponse;const total=(await response.json()).payload;await settled(p);const s=await screen(p,info,'CO-scope-individual-nonempty-vendor');const expected=all.filter((x:any)=>x.vendorName===candidate.vendor.vendorName);const ok=Number(total.totalCount)===expected.length&&Number(total.totalAmount)===expected.reduce((sum:number,x:any)=>sum+x.amount,0)&&new URL(response.url()).searchParams.get('departmentId')===candidate.vendor.vendorId;put('R01',ok,`전체 조회의 실제 ${expected.length}개 거래를 선택 가맹점 조회의 건수·매출합계·요청 범위와 대조`,[allShot,s]);
    const other=branches.find((x:any)=>x.departmentId!==candidate.branch.departmentId);if(other){const nextRequest=p.waitForRequest(r=>new URL(r.url()).pathname==='/api/v1/store/transactions'&&new URL(r.url()).searchParams.get('departmentId')===other.departmentId);await branchLabel.getByRole('button').click();await p.getByRole('option',{name:other.departmentName,exact:true}).click();await nextRequest;await settled(p);const reset=await screen(p,info,'CO-scope-next-branch-query');put('R02',await p.locator('select').nth(1).inputValue()==='','영업점 변경 시 가맹점 선택 초기화 및 새 영업점 범위의 거래 요청·화면 갱신 확인',[reset]);}
   }else blocked(rows,role,'R01','전월 전체 거래는 조회했으나 하위 선택 목록과 대조 가능한 거래 가맹점이 없음',[allShot]);
  }
  let baselineText='';let baselineContent:any;const screenshots:string[]=[];
  for(const st of [true,false]){if(!st)await toggle(false);await go(p,'/sales-summary');const response=p.waitForResponse(r=>new URL(r.url()).pathname==='/api/v1/store/profit/branch',{timeout:60000});await p.getByRole('button',{name:'전월',exact:true}).click();await p.getByRole('button',{name:'조회',exact:true}).click();const content=(await(await response).json()).payload?.content??[];await settled(p);const text=await p.locator('body').innerText();if(await p.locator('table').count())await p.locator('table').locator('..').evaluate(e=>{e.scrollLeft=e.scrollWidth});screenshots.push(await screen(p,info,`${role}-previous-month-profit-settlement-${st?'on':'off'}`));if(st){baselineText=text;baselineContent=content;}else if(content.length&&baselineContent.length){put('C07',text===baselineText&&JSON.stringify(content)===JSON.stringify(baselineContent)&&text.includes('수수료')&&text.includes('정산금')&&text.includes('수익'),`전월 실제 매출집계 ${content.length}개 행의 수수료·정산·수익 표시 및 원본 조회 결과가 정산 권한 ON/OFF에서 동일`,screenshots);}else blocked(rows,role,'C07','매출집계 메뉴·0원 요약 유지 확인. 전월 실데이터 행이 없어 수수료·수익금 실제 행 대조 불가',screenshots);}
 }catch(error){if(/Timeout|ERR_CONNECTION|ERR_HTTP2/.test(String(error)))throw new Error(`BLOCKED: ${String(error)}`);throw error;}finally{try{await openPreset(a,role);await save(a,role,{'스토어 거래조회':selected(original).includes(keys['스토어 거래조회']),'스토어 정산조회':selected(original).includes(keys['스토어 정산조회']),'매출집계 조회':selected(original).includes(keys['매출집계 조회'])},'전월 매출집계·조회 범위 검증 원복');expect(selected(await preset(c,role))).toEqual(selected(original));await refresh(p);await settled(p);await screen(p,info,`${role}-profit-scope-restored`);}finally{persist(role,rows);await p.context().close();await c.close();}}
});
