import { expect, test } from './authenticated-test.js';

/** File purpose: exercise the device route, readable rows, client image limit, and create/edit requests without a real database. */
test('shows the device management page and readable device row', async ({ page }) => {
  /** Plain purpose: provide stable device-list and person-lookup responses. Related files: DeviceAssetView.js, device-api-client.js. Flow: browser request -> route.fulfill -> Vue table/dropdown. */
  await page.route('**/api/device-assets**', async route => {
    const url=route.request().url();
    const data=url.includes('/lookups/company-persons')?[{id:9,personName:'\u5f20\u4e09'}]:{records:[{id:1,deviceName:'iPhone 15-01',imageAttachment1Url:null,imageAttachment2Url:null,userPersonId:9,userPersonName:'\u5f20\u4e09',userUsageStatus:'\u4f7f\u7528\u4e2d',assetRelationStatus:'\u5df2\u5173\u8054',createTime:'2026-08-01T10:00:00',updateTime:'2026-08-01T10:00:00'}],total:1,page:1,size:20};
    await route.fulfill({contentType:'application/json',body:JSON.stringify({code:200,message:'success',data})});
  });
  await page.goto('/asset/#/device-assets');
  await expect(page.getByRole('heading',{name:'\u8bbe\u5907\u8d44\u4ea7\u7ba1\u7406'})).toBeVisible();
  await expect(page.getByText('iPhone 15-01')).toBeVisible();
  await expect(page.getByText('\u5f20\u4e09(9)')).toBeVisible();
  await expect(page.locator('.el-table').getByText('\u4f7f\u7528\u4e2d',{exact:true})).toBeVisible();
  await expect(page.locator('.el-table').getByText('\u5df2\u5173\u8054',{exact:true})).toBeVisible();
});

/** Plain purpose: ensure an image over 20MB is rejected before a device save request. Related files: DeviceAssetView.js, DeviceAssetFileStorageService.java. Flow: choose file -> client validation -> message and no POST. */
test('rejects an oversized image before device save', async ({ page }) => {
  /** Plain purpose: provide an empty device page so the create dialog can be opened. Related files: DeviceAssetView.js, device-api-client.js. Flow: GET -> route response -> add dialog. */
  await page.route('**/api/device-assets**', async route=>route.fulfill({contentType:'application/json',body:JSON.stringify({code:200,message:'success',data:{records:[],total:0,page:1,size:20}})}));
  await page.goto('/asset/#/device-assets');
  await page.getByRole('button',{name:'\u65b0\u589e\u8bbe\u5907'}).click();
  await page.locator('input[type=file]').first().setInputFiles({name:'too-large.png',mimeType:'image/png',buffer:Buffer.alloc(20*1024*1024+1)});
  await expect(page.getByText('\u6bcf\u5f20\u56fe\u7247\u4e0d\u80fd\u8d85\u8fc7 20MB')).toBeVisible();
});

/** Plain purpose: verify dialog save sends POST for new devices and PUT for edits. Related files: DeviceAssetView.js, device-api-client.js. Flow: dialog save -> fetch POST/PUT -> API response -> list refresh. */
test('creates and edits a device asset through the dialog', async ({ page }) => {
  let createRequested=false,updateRequested=false,csrfRequested=false,createCsrfHeader='',createContentType='';
  /** Plain purpose: simulate a missing browser CSRF Cookie and issue a replacement token before the device write. Related files: auth-api-client.js, SecurityConfig.java. Flow: missing Cookie -> GET csrf -> replacement Cookie -> protected multipart POST. */
  await page.context().clearCookies();
  await page.route('**/api/auth/csrf', async route=>{csrfRequested=true;await page.context().addCookies([{name:'XSRF-TOKEN',value:'refreshed-csrf',domain:'127.0.0.1',path:'/'}]);await route.fulfill({contentType:'application/json',body:JSON.stringify({code:200,message:'success',data:'refreshed-csrf'})});});
  /** Plain purpose: return list data and record which save method was sent. Related files: DeviceAssetView.js, device-api-client.js. Flow: form submit -> route interception -> successful JSON -> refreshed table. */
  await page.route('**/api/device-assets**', async route=>{const request=route.request();if(request.method()==='POST'){createRequested=true;createCsrfHeader=request.headers()['x-xsrf-token']||'';createContentType=request.headers()['content-type']||'';}if(request.method()==='PUT')updateRequested=true;const data=request.method()==='GET'?{records:[{id:1,deviceName:'iPhone 15-01',imageAttachment1Url:null,imageAttachment2Url:null,userPersonId:null,userPersonName:null,userUsageStatus:'\u4f7f\u7528\u4e2d',assetRelationStatus:'\u5f85\u786e\u8ba4',createTime:'',updateTime:''}],total:1,page:1,size:20}:{id:1,deviceName:'iPhone 15-01'};await route.fulfill({contentType:'application/json',body:JSON.stringify({code:200,message:'success',data})});});
  await page.goto('/asset/#/device-assets');
  await page.getByRole('button',{name:'\u65b0\u589e\u8bbe\u5907'}).click();
  await page.getByRole('dialog').getByRole('textbox').first().fill('\u65b0\u589e\u6d4b\u8bd5\u8bbe\u5907');
  await page.getByRole('dialog').getByRole('button',{name:'\u4fdd\u5b58'}).click();
  await expect.poll(()=>createRequested).toBe(true);
  await expect.poll(()=>csrfRequested).toBe(true);
  expect(createCsrfHeader).toBe('refreshed-csrf');
  expect(createContentType).toContain('multipart/form-data; boundary=');
  await page.locator('.el-table').getByRole('button',{name:'\u7f16\u8f91'}).click();
  await page.getByRole('dialog').getByRole('textbox').first().fill('\u7f16\u8f91\u540e\u7684\u8bbe\u5907');
  await page.getByRole('dialog').getByRole('button',{name:'\u4fdd\u5b58'}).click();
  await expect.poll(()=>updateRequested).toBe(true);
});

/** Plain purpose: verify an invalidated server session clears local identity and returns the browser to login without retrying the failed list request. Related files: auth-api-client.js, auth-store.js. Flow: device GET 401 -> auth-expired event -> auth state clear -> login route. */
test('redirects to login after an invalid device session', async ({ page }) => {
  /** Plain purpose: return an expired-session response for the first device page request. Related files: SecurityConfig.java, auth-api-client.js. Flow: GET device list -> 401 envelope -> session-expired event. */
  await page.route('**/api/device-assets**', async route=>route.fulfill({status:401,contentType:'application/json',body:JSON.stringify({code:401,message:'\u8bf7\u5148\u767b\u5f55',data:null})}));
  await page.goto('/asset/#/device-assets');
  await expect(page).toHaveURL(/#\/login$/);
});

/** Plain purpose: verify fixed status options are shown and an unsupported file never becomes an upload value. Related files: DeviceAssetView.js, DeviceAssetFileStorageService.java. Flow: open form -> status dropdown/file choose -> client validation message. */
test('shows fixed statuses and rejects an unsupported image type', async ({ page }) => {
  /** Plain purpose: return an empty list to isolate the create dialog. Related files: DeviceAssetView.js, device-api-client.js. Flow: GET -> empty table -> create dialog. */
  await page.route('**/api/device-assets**', async route=>route.fulfill({contentType:'application/json',body:JSON.stringify({code:200,message:'success',data:{records:[],total:0,page:1,size:20}})}));
  await page.goto('/asset/#/device-assets');
  await page.getByRole('button',{name:'\u65b0\u589e\u8bbe\u5907'}).click();
  await page.getByRole('dialog').locator('.el-select').first().click();
  expect(await page.getByText('\u4f7f\u7528\u4e2d',{exact:true}).allTextContents()).toContain('\u4f7f\u7528\u4e2d');
  expect(await page.getByText('\u95f2\u7f6e',{exact:true}).allTextContents()).toContain('\u95f2\u7f6e');
  expect(await page.getByText('\u7ef4\u4fee\u4e2d',{exact:true}).allTextContents()).toContain('\u7ef4\u4fee\u4e2d');
  expect(await page.getByText('\u505c\u7528',{exact:true}).allTextContents()).toContain('\u505c\u7528');
  await page.locator('input[type=file]').first().setInputFiles({name:'not-image.txt',mimeType:'text/plain',buffer:Buffer.from('not an image')});
  await expect(page.getByText('\u4ec5\u652f\u6301 JPG\u3001PNG\u3001GIF \u56fe\u7247')).toBeVisible();
});

/** Plain purpose: verify a protected delete keeps the row visible and displays the API reason. Related files: DeviceAssetView.js, DeviceAssetService.java. Flow: delete confirmation -> DELETE 400 -> Element Plus message -> unchanged table row. */
test('keeps a referenced device visible after protected delete response', async ({ page }) => {
  /** Plain purpose: simulate one active row and a server-side reference-protection error. Related files: device-api-client.js, DeviceAssetController.java. Flow: DELETE -> error envelope -> page message. */
  await page.route('**/api/device-assets**', async route=>{const request=route.request();const data=request.method()==='GET'?{records:[{id:8,deviceName:'protected-device',imageAttachment1Url:null,imageAttachment2Url:null,userPersonId:null,userPersonName:null,userUsageStatus:'\u4f7f\u7528\u4e2d',assetRelationStatus:'\u5df2\u5173\u8054',createTime:'',updateTime:''}],total:1,page:1,size:20}:null;const response=request.method()==='DELETE'?{code:400,message:'\u8bbe\u5907\u4ecd\u88ab\u624b\u673a\u53f7\u8d44\u4ea7\u5f15\u7528\uff0c\u4e0d\u80fd\u5220\u9664',data:null}:{code:200,message:'success',data};await route.fulfill({contentType:'application/json',body:JSON.stringify(response)});});
  await page.goto('/asset/#/device-assets');
  await page.locator('.el-table').getByRole('button',{name:'\u5220\u9664'}).click();
  await page.getByRole('button',{name:'OK'}).click();
  await expect(page.getByText('\u8bbe\u5907\u4ecd\u88ab\u624b\u673a\u53f7\u8d44\u4ea7\u5f15\u7528\uff0c\u4e0d\u80fd\u5220\u9664')).toBeVisible();
  await expect(page.locator('.el-table').getByText('protected-device',{exact:true})).toBeVisible();
});
