/**
 * Copyright (c) Microsoft Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import fs from 'fs';
import path from 'path';

import { test, expect, eventsPage } from './cli-fixtures';

test('open', async ({ cli, server }) => {
  const { output, snapshot } = await cli('open', server.HELLO_WORLD);
  expect(output).toContain(`### Page
- Page URL: ${server.HELLO_WORLD}
- Page Title: Title`);

  expect(snapshot).toContain(`- generic [active] [ref=e1]: Hello, world!`);
});

test('close', async ({ cli, server }) => {
  await cli('open', server.HELLO_WORLD);
  const { output } = await cli('close');
  expect(output).toContain(`Browser 'default' closed`);
});

test('click button', async ({ cli, server }) => {
  server.setContent('/', `<button>Submit</button>`, 'text/html');

  const { snapshot } = await cli('open', server.PREFIX);
  expect(snapshot).toContain(`- button "Submit" [ref=e2]`);

  const { output, snapshot: clickSnapshot } = await cli('click', 'e2');
  expect(clickSnapshot).toBeTruthy();
  expect(output).toContain(`### Ran Playwright code
\`\`\`js
await page.getByRole('button', { name: 'Submit' }).click();
\`\`\``);
});

test('click link', async ({ cli, server }) => {
  server.setContent('/', `<a href="/hello-world">Hello, world!</a>`, 'text/html');

  const { snapshot } = await cli('open', server.PREFIX);
  expect(snapshot).toContain(`- link \"Hello, world!\" [ref=e2]`);

  const { output: clickOutput, snapshot: clickSnapshot } = await cli('click', 'e2');
  expect(clickOutput).toContain(`### Page
- Page URL: ${server.HELLO_WORLD}
- Page Title: Title`);
  expect(clickSnapshot).toContain('Hello, world!');
});

test('dblclick', async ({ cli, server }) => {
  server.setContent('/', eventsPage, 'text/html');
  await cli('open', server.PREFIX);
  const { snapshot } = await cli('dblclick', 'e2');
  expect(snapshot).toContain('dblclick 0');
});

test('type', async ({ cli, server }) => {
  server.setContent('/', `<input type=text>`, 'text/html');
  const { snapshot } = await cli('open', server.PREFIX);
  expect(snapshot).toContain(`- textbox [ref=e2]`);

  const { snapshot: typeSnapshot } = await cli('type', 'Hello, world!', '--submit');
  expect(typeSnapshot).toBe(`- textbox [ref=e2]`);
});

test('fill', async ({ cli, server }) => {
  server.setContent('/', `<input type=text>`, 'text/html');
  const { snapshot } = await cli('open', server.PREFIX);
  expect(snapshot).toContain(`- textbox [ref=e2]`);

  const { snapshot: fillSnapshot } = await cli('fill', 'e2', 'Hello, world!', '--submit');
  expect(fillSnapshot).toBe(`- textbox [active] [ref=e2]: Hello, world!`);
});

test('fill numeric', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright-cli/issues/235' } }, async ({ cli, server }) => {
  server.setContent('/', `<input type=text>`, 'text/html');
  const { snapshot } = await cli('open', server.PREFIX);
  expect(snapshot).toContain(`- textbox [ref=e2]`);

  const { snapshot: fillSnapshot } = await cli('fill', 'e2', '42', '--submit');
  expect(fillSnapshot).toContain(`[ref=e2]: "42"`);
});

test('hover', async ({ cli, server }) => {
  server.setContent('/', eventsPage, 'text/html');
  await cli('open', server.PREFIX);
  await cli('hover', 'e2');
  const { inlineSnapshot } = await cli('snapshot');
  expect(inlineSnapshot).toContain('mouse move 50 50');
});

test('select', async ({ cli, server }) => {
  server.setContent('/', `<select><option value="1">One</option><option value="2">Two</option></select>`, 'text/html');
  await cli('open', server.PREFIX);
  await cli('select', 'e2', 'Two');
  const { inlineSnapshot } = await cli('snapshot');
  expect(inlineSnapshot).toContain('- option "Two" [selected]');
});

test('check', async ({ cli, server, mcpBrowser }) => {
  const active = mcpBrowser === 'webkit' && process.platform !== 'linux' ? '' : '[active] ';
  server.setContent('/', `<input type="checkbox">`, 'text/html');
  await cli('open', server.PREFIX);
  await cli('check', 'e2');
  const { inlineSnapshot } = await cli('snapshot');
  expect(inlineSnapshot).toContain(`- checkbox [checked] ${active}[ref=e2]`);
});

test('uncheck', async ({ cli, server, mcpBrowser }) => {
  const active = mcpBrowser === 'webkit' && process.platform !== 'linux' ? '' : '[active] ';
  server.setContent('/', `<input type="checkbox" checked>`, 'text/html');
  await cli('open', server.PREFIX);
  await cli('uncheck', 'e2');
  const { inlineSnapshot } = await cli('snapshot');
  expect(inlineSnapshot).toContain(`- checkbox ${active}[ref=e2]`);
});

test('eval', async ({ cli, server }) => {
  await cli('open', server.HELLO_WORLD);
  const { output } = await cli('eval', '() => document.title');
  expect(output).toContain('"Title"');
});

test('eval no arrow', async ({ cli, server }) => {
  await cli('open', server.HELLO_WORLD);
  const { output } = await cli('eval', 'document.title');
  expect(output).toContain('"Title"');
});

test('eval <ref>', async ({ cli, server }) => {
  server.setContent('/', `<button>Submit</button>`, 'text/html');
  await cli('open', server.PREFIX);
  const { output } = await cli('eval', 'element => element.nodeName', 'e2');
  expect(output).toContain('"BUTTON"');
});

test('dialog-accept', async ({ cli, server }) => {
  server.setContent('/', `<button onclick="alert('MyAlert')">Button</button>`, 'text/html');
  await cli('open', server.PREFIX);
  const { output } = await cli('click', 'e2');
  expect(output).toContain('MyAlert');
  expect(output).toContain('["alert" dialog with message "MyAlert"]: can be handled by dialog-accept or dialog-dismiss');
  await cli('dialog-accept');
  const { inlineSnapshot } = await cli('snapshot');
  expect(inlineSnapshot).not.toContain('MyAlert');
});

test('dialog-dismiss', async ({ cli, server }) => {
  server.setContent('/', `<button onclick="alert('MyAlert')">Button</button>`, 'text/html');
  await cli('open', server.PREFIX);
  const { output } = await cli('click', 'e2');
  expect(output).toContain('MyAlert');
  await cli('dialog-dismiss');
  const { inlineSnapshot } = await cli('snapshot');
  expect(inlineSnapshot).not.toContain('MyAlert');
});

test('dialog-accept <prompt>', async ({ cli, server }) => {
  server.setContent('/', `<button onclick="document.body.textContent = prompt('MyAlert')">Button</button>`, 'text/html');
  await cli('open', server.PREFIX);
  await cli('click', 'e2');
  await cli('dialog-accept', 'my reply');
  const { inlineSnapshot } = await cli('snapshot');
  expect(inlineSnapshot).toContain('my reply');
});

test('resize', async ({ cli, server }) => {
  await cli('open', server.PREFIX);
  await cli('resize', '480', '320');
  const { output } = await cli('eval', '() => window.innerWidth + "x" + window.innerHeight');
  expect(output).toContain('480x320');
});

test('snapshot', async ({ cli, server }, testInfo) => {
  {
    const { output } = await cli('open', server.HELLO_WORLD);
    expect(output).toContain('.playwright-cli' + path.sep + 'page-');
  }
  {
    const nested = testInfo.outputPath('nested');
    await fs.promises.mkdir(nested, { recursive: true });
    const { output } = await cli('goto', server.HELLO_WORLD, { cwd: nested });
    expect(output).toContain('..' + path.sep + '.playwright-cli' + path.sep + 'page-');
  }
});

test('click in iframe', async ({ cli, server }) => {
  server.setContent('/', `<h1>Hello</h1><iframe src="data:text/html,<button>World</button><main><iframe src='data:text/html,<p>Nested</p>'></iframe></main>"></iframe><iframe src="data:text/html,<h1>Should be invisible</h1>" style="display: none;"></iframe>`, 'text/html');

  const { snapshot } = await cli('open', server.PREFIX);
  expect(snapshot).toContain(`- button "World" [ref=f1e2]`);

  const { output, snapshot: clickSnapshot } = await cli('click', 'f1e2');
  expect(clickSnapshot).toBeTruthy();
  expect(output).toContain(`### Ran Playwright code
\`\`\`js
await page.locator('iframe').first().contentFrame().getByRole('button', { name: 'World' }).click();
\`\`\``);
});

test('click button with CSS selector', async ({ cli, server }) => {
  server.setContent('/', `<button>Submit</button>`, 'text/html');

  const { snapshot } = await cli('open', server.PREFIX);
  expect(snapshot).toContain(`- button "Submit" [ref=e2]`);

  const { output, snapshot: clickSnapshot } = await cli('click', 'button');
  expect(clickSnapshot).toBeTruthy();
  expect(output).toContain(`### Ran Playwright code
\`\`\`js
await page.locator('button').click();
\`\`\``);
});

test('click button with role locator', async ({ cli, server }) => {
  server.setContent('/', `<button>Submit</button>`, 'text/html');

  const { snapshot } = await cli('open', server.PREFIX);
  expect(snapshot).toContain(`- button "Submit" [ref=e2]`);

  const { output, snapshot: clickSnapshot } = await cli('click', 'getByRole("button", { name: "Submit" })');
  expect(clickSnapshot).toBeTruthy();
  expect(output).toContain(`### Ran Playwright code
\`\`\`js
await page.getByRole('button', { name: 'Submit' }).click();
\`\`\``);
});

test('click button with test id locator', async ({ cli, server }) => {
  server.setContent('/', `<div data-testid=main><button>Submit</button></div>`, 'text/html');

  const { snapshot } = await cli('open', server.PREFIX);
  expect(snapshot).toContain(`- button "Submit" [ref=e3]`);

  const { output, snapshot: clickSnapshot } = await cli('click', 'getByTestId("main").getByRole("button")');
  expect(clickSnapshot).toBeTruthy();
  expect(output).toContain(`### Ran Playwright code
\`\`\`js
await page.getByTestId('main').getByRole('button').click();
\`\`\``);
});

test('click button with wrong css selector', async ({ cli, server }) => {
  server.setContent('/', `<button>Submit</button>`, 'text/html');

  const { snapshot } = await cli('open', server.PREFIX);
  expect(snapshot).toContain(`- button "Submit" [ref=e2]`);

  const { output } = await cli('click', '#target');
  expect(output).toContain(`"#target" does not match any elements.`);
});

test('partial snapshot', async ({ cli, server }) => {
  server.setContent('/', `<button id=one>Submit</button><button id=two>Cancel</button>`, 'text/html');
  const { snapshot } = await cli('open', server.PREFIX);
  expect(snapshot).toContain('- button "Submit" [ref=e2]');
  expect(snapshot).toContain('- button "Cancel" [ref=e3]');

  const { inlineSnapshot: partialSnapshot } = await cli('snapshot', '#two');
  expect(partialSnapshot).toBe(`- button "Cancel" [ref=e3]`);

  const { output: strictError } = await cli('snapshot', 'button');
  expect(strictError).toContain(`strict mode violation`);

  const { output: noMatchError } = await cli('snapshot', '#target');
  expect(noMatchError).toContain(`"#target" does not match any element`);
});

test('partial snapshot by ref', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright-cli/issues/347' } }, async ({ cli, server }) => {
  server.setContent('/', `<button id=one>Submit</button><button id=two>Cancel</button>`, 'text/html');
  const { snapshot } = await cli('open', server.PREFIX);
  expect(snapshot).toContain('- button "Submit" [ref=e2]');
  expect(snapshot).toContain('- button "Cancel" [ref=e3]');

  const { inlineSnapshot: partialSnapshot } = await cli('snapshot', 'e3');
  expect(partialSnapshot).toBe(`- button "Cancel" [ref=e3]`);

  const { output: missingRefError } = await cli('snapshot', 'e999');
  expect(missingRefError).toContain(`Ref e999 not found in the current page snapshot.`);
});

test('snapshot depth', async ({ cli, server }) => {
  server.setContent('/', `<ul><li><button id=one>Submit</button></li><li><button id=two>Cancel</button></li></ul>`, 'text/html');
  await cli('open', server.PREFIX);

  const { inlineSnapshot: limitedSnapshot } = await cli('snapshot', '--depth=1');
  expect(limitedSnapshot).toBe(`- list [ref=e2]:
  - listitem [ref=e3]
  - listitem [ref=e5]`);

  const { inlineSnapshot: fullSnapshot } = await cli('snapshot', '--depth=100');
  expect(fullSnapshot).toBe(`- list [ref=e2]:
  - listitem [ref=e3]:
    - button "Submit" [ref=e4]
  - listitem [ref=e5]:
    - button "Cancel" [ref=e6]`);
});

test('snapshot --boxes', async ({ cli, server }) => {
  server.setContent('/', `
    <style>body { margin: 0; }</style>
    <button style="position:absolute;left:100px;top:50px;width:80px;height:40px;margin:0;padding:0;border:0;">click</button>
  `, 'text/html');
  await cli('open', server.PREFIX);

  const { inlineSnapshot } = await cli('snapshot', '--boxes');
  expect(inlineSnapshot).toContain(`- button "click" [ref=e1] [box=100,50,80,40]`);

  const { inlineSnapshot: plain } = await cli('snapshot');
  expect(plain).not.toMatch(/\[box=/);
});

test('eval --raw', async ({ cli, server }) => {
  await cli('open', server.HELLO_WORLD);
  const { output } = await cli('eval', '--raw', '() => document.title');
  expect(output).toBe('"Title"');
});

test('eval --raw with error', async ({ cli, server }) => {
  await cli('open', server.HELLO_WORLD);
  const { output } = await cli('eval', '--raw', '() => { throw new Error("my error") }');
  expect(output).toContain('my error');
  expect(output).not.toContain('##');
});

test('snapshot --raw', async ({ cli, server }) => {
  server.setContent('/', `<button>Submit</button>`, 'text/html');
  await cli('open', server.PREFIX);
  const { output } = await cli('snapshot', '--raw');
  expect(output).toBe('- button "Submit" [ref=e2]');
});

test('--raw on command without output', async ({ cli, server }) => {
  server.setContent('/', `<button>Submit</button>`, 'text/html');
  await cli('open', server.PREFIX);
  const { output } = await cli('click', '--raw', 'e2');
  expect(output).not.toContain('### ');
  expect(output).not.toContain('Page URL');
});
