import {test,expect,type BrowserContext,type Page} from '@playwright/test';
import fs from 'node:fs';
import {env,privateRoot,backup,route,open,field,select,choose,shot,proof,profile,dismiss} from './helpers';
const vendor=process.env.QA4420_VENDOR_NAME??'김경주_가맹_D+1(1일 후)';
let ctx:BrowserContext,page:Page,allowedId='',original:any,dirty=false;
test.use({channel:'chrome',video:'off',screenshot:'off',trace:'retain-on-failure'});
test.beforeAll(async({browser})=>{ctx=await browser.newContext({storageState:env().storageState,viewport:{width:1440,height:1100}});page=await ctx.newPage();page.setDefaultTimeout(10000);const role=page.waitForResponse(r=>new URL(r.url()).pathname.endsWith('/auth/me')&&r.status()===200);await page.goto(env().baseUrl+'/?tenantId='+env().tenantId);expect((await(await role).json()).payload.role).toBe('ADMIN');
 await page.route('**/api/**',async r=>{const req=r.request(),p=new URL(req.url()).pathname;if(['POST','PATCH','PUT','DELETE'].includes(req.method())){if(req.method()!=='PATCH'||!allowedId||!p.endsWith('/v2/vendors/'+allowedId)||process.env.QA4420_ALLOW_VENDOR_MUTATION!=='1')return r.abort('blockedbyclient');dirty=true;backup('request-'+Date.now(),req.postDataJSON());}return r.continue();});
});
test.afterAll(async()=>{try{if(dirty&&original)await restore();}finally{await ctx?.close();}});
async function existing(){const x=await open(page,'vendor-manage',vendor);expect(x.detail.code).toBe('BP100611');allowedId=x.id;if(!original){const saved='qa-results/issues/4420/dev/baseline-original.json';original=fs.existsSync(saved)?JSON.parse(fs.readFileSync(saved,'utf8')):x.detail;fs.writeFileSync(saved,JSON.stringify(original,null,2));backup('original',original);}return x;}
async function save(form:any){await form.getByRole('button',{name:'수정',exact:true}).click();const reason=page.getByPlaceholder('변경사유',{exact:true});await expect(reason).toBeVisible();await reason.fill('QA4420 휴일 이체 설정 검증 및 원복');const w=page.waitForResponse(r=>r.request().method()==='PATCH'&&new URL(r.url()).pathname.endsWith('/v2/vendors/'+allowedId));await page.locator('.swal2-popup:visible').getByRole('button',{name:'확인',exact:true}).click();const res=await w;expect(res.status()).toBe(200);await expect(form).not.toBeVisible();await page.reload();return existing();}
async function restore(){await page.setViewportSize({width:1440,height:1100});let x=await existing();await choose(page,x.form,original.holidayTransferEnabled);await x.form.locator('[name="note"]').fill(original.businessInfo.note??'');x=await save(x.form);expect(x.detail.holidayTransferEnabled).toBe(original.holidayTransferEnabled);expect(x.detail.businessInfo.note).toBe(original.businessInfo.note);expect(profile(x.detail)).toEqual(profile(original));await expect(field(x.form)).toHaveValue(String(original.holidayTransferEnabled));backup('restored',{holidayTransferEnabled:x.detail.holidayTransferEnabled,noteRestored:true,otherConfigurationRestored:true});dirty=false;return x;}
test.describe('[dev] [ADMIN] 가맹점 휴일 지갑 이체',()=>{
 test('A01 일반정보의 중복 휴일 이체 사용 여부 미표시',async({},info)=>{const {form}=await existing();await expect(form.getByText('휴일 이체 사용 여부',{exact:true})).toHaveCount(0);await shot(form,info,'A01-general');});
 test('A02 정산정보 휴일 지갑 이체 1회 표시',async({},info)=>{const {form}=await existing();await expect(select(form)).toHaveCount(1);await expect(field(form)).toHaveCount(1);await select(form).scrollIntoViewIfNeeded();await shot(form,info,'A02-settlement');});
 test('A03 기존 설정·새로고침 재진입 유지',async({},info)=>{let x=await existing();const before=x.detail.holidayTransferEnabled;await expect(field(x.form)).toHaveValue(String(before));await page.reload();x=await existing();expect(x.detail.holidayTransferEnabled).toBe(before);await expect(field(x.form)).toHaveValue(String(before));await select(x.form).scrollIntoViewIfNeeded();await shot(x.form,info,'A03-reentry');await proof(info,{existingValue:before,afterReload:before});});
 test('A04 비고만 저장 후 휴일 지갑 이체 유지',async({},info)=>{test.setTimeout(90000);test.skip(process.env.QA4420_ALLOW_VENDOR_MUTATION!=='1','BLOCKED: 승인 대상 저장 guard 필요');try{let x=await existing();await x.form.locator('[name="note"]').fill('QA4420 비고만 수정 검증');x=await save(x.form);expect(x.detail.holidayTransferEnabled).toBe(original.holidayTransferEnabled);expect(x.detail.businessInfo.note).toBe('QA4420 비고만 수정 검증');expect(profile(x.detail)).toEqual(profile(original));await select(x.form).scrollIntoViewIfNeeded();await shot(x.form,info,'A04-note-only');await proof(info,{noteOnlySave:true,holidayUnchanged:true,otherSettingsUnchanged:true});}finally{if(dirty)await restore();}});
 test('A05 사용→미사용 저장·새로고침 재진입',async({},info)=>{test.setTimeout(100000);test.skip(process.env.QA4420_ALLOW_VENDOR_MUTATION!=='1','BLOCKED: 승인 대상 저장 guard 필요');try{let x=await existing();if(!x.detail.holidayTransferEnabled){await choose(page,x.form,true);x=await save(x.form);expect(x.detail.holidayTransferEnabled).toBe(true);}await choose(page,x.form,false);x=await save(x.form);expect(x.detail.holidayTransferEnabled).toBe(false);await expect(field(x.form)).toHaveValue('false');expect(profile(x.detail)).toEqual(profile(original));await select(x.form).scrollIntoViewIfNeeded();await shot(x.form,info,'A05-disabled');await proof(info,{from:true,to:false,saveAndReloadVerified:true});}finally{if(dirty)await restore();}});
 test('A06 미사용→사용 저장·새로고침 재진입',async({},info)=>{test.setTimeout(100000);test.skip(process.env.QA4420_ALLOW_VENDOR_MUTATION!=='1','BLOCKED: 승인 대상 저장 guard 필요');try{let x=await existing();if(x.detail.holidayTransferEnabled){await choose(page,x.form,false);x=await save(x.form);}await choose(page,x.form,true);x=await save(x.form);expect(x.detail.holidayTransferEnabled).toBe(true);await expect(field(x.form)).toHaveValue('true');expect(profile(x.detail)).toEqual(profile(original));await select(x.form).scrollIntoViewIfNeeded();await shot(x.form,info,'A06-enabled');await proof(info,{from:false,to:true,saveAndReloadVerified:true});}finally{if(dirty)await restore();}});
 test('A07 생성 모달 중복 제거·휴일 선택 가능',async({},info)=>{await route(page);const a=page.locator('[data-route-keep-alive-active=true]');await a.getByText('생성',{exact:true}).click();const form=page.getByRole('dialog').filter({has:page.locator('input[name="name"]')}).first();await expect(form).toBeVisible();await expect(form.getByText('휴일 이체 사용 여부',{exact:true})).toHaveCount(0);await expect(select(form)).toHaveCount(1);await choose(page,form,false);await choose(page,form,true);await select(form).scrollIntoViewIfNeeded();await shot(form,info,'A07-create-no-save');await form.getByRole('button',{name:'취소',exact:true}).click();await expect(form).not.toBeVisible();await proof(info,{createdNewVendor:false,selectionBothDirections:true});});
 for(const [key,entity,name,label] of [['A08','corporation','김경주_법인','휴일 지갑 이체'],['A09','branch','김경주_영업','휴일 이체 사용 여부']])test(`${key} ${name} 기존 휴일 항목·설정 유지`,async({},info)=>{let x=await open(page,entity,name);backup(entity+'-original',x.detail);const before=x.detail.holidayTransferEnabled;await expect(select(x.form,label)).toHaveCount(1);await expect(field(x.form)).toHaveValue(String(before));await page.reload();x=await open(page,entity,name);expect(x.detail.holidayTransferEnabled).toBe(before);await expect(field(x.form)).toHaveValue(String(before));await select(x.form,label).scrollIntoViewIfNeeded();await shot(x.form,info,key+'-reentry');await proof(info,{target:name,holidayBefore:before,holidayAfterReload:x.detail.holidayTransferEnabled,writes:0});});
 test('A10 PC·모바일 너비 수정 모달 배치·개폐',async({},info)=>{test.setTimeout(90000);const measurements=[];for(const width of [1440,390]){await page.setViewportSize({width,height:1100});let {form}=await existing();await expect(form.getByRole('button',{name:'취소',exact:true})).toBeVisible();await expect(form.getByRole('button',{name:'수정',exact:true})).toBeVisible();await select(form).scrollIntoViewIfNeeded();const box=await select(form).boundingBox();expect(box).not.toBeNull();expect(box!.x).toBeGreaterThanOrEqual(0);expect(box!.x+box!.width).toBeLessThanOrEqual(width+1);const buttons=await form.getByRole('button',{name:/^(취소|수정)$/}).evaluateAll(xs=>xs.map(x=>{const b=x.getBoundingClientRect();return{x:b.x,y:b.y,w:b.width,h:b.height,text:x.textContent};}));expect(buttons).toHaveLength(2);expect(buttons[0].x+buttons[0].w<=buttons[1].x||buttons[1].x+buttons[1].w<=buttons[0].x||buttons[0].y+buttons[0].h<=buttons[1].y||buttons[1].y+buttons[1].h<=buttons[0].y).toBeTruthy();await shot(form,info,'A10-width-'+width);measurements.push({width,holiday:box,buttons});await form.getByRole('button',{name:'취소',exact:true}).click();await expect(form).not.toBeVisible();await proof(info,measurements);for(const b of buttons){expect(b.x,`${width}px ${b.text} left bound`).toBeGreaterThanOrEqual(0);expect(b.x+b.w,`${width}px ${b.text} right bound`).toBeLessThanOrEqual(width+1);}}await page.setViewportSize({width:1440,height:1100});});
 test('A11 최종 원본 설정·비고 복구·재조회',async({},info)=>{test.setTimeout(90000);if(dirty)await restore();const x=await existing();expect(x.detail.holidayTransferEnabled).toBe(original.holidayTransferEnabled);expect(x.detail.businessInfo.note).toBe(original.businessInfo.note);expect(profile(x.detail)).toEqual(profile(original));await expect(field(x.form)).toHaveValue(String(original.holidayTransferEnabled));await select(x.form).scrollIntoViewIfNeeded();await shot(x.form,info,'A11-restored');await proof(info,{originalHoliday:original.holidayTransferEnabled,finalHoliday:x.detail.holidayTransferEnabled,noteRestored:true,otherConfigurationRestored:true,dirty:false});});
});
