/**
 * 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 * as z from 'zod';
import { formatObjectOrVoid } from '@isomorphic/stringUtils';
import { defineTabTool } from './tool';

const mouseMove = defineTabTool({
  capability: 'vision',
  schema: {
    name: 'browser_mouse_move_xy',
    title: 'Move mouse',
    description: 'Move mouse to a given position',
    inputSchema: z.object({
      x: z.number().describe('X coordinate'),
      y: z.number().describe('Y coordinate'),
    }),
    type: 'input',
  },

  handle: async (tab, params, response) => {
    response.addCode(`// Move mouse to (${params.x}, ${params.y})`);
    response.addCode(`await page.mouse.move(${params.x}, ${params.y});`);

    await tab.page.mouse.move(params.x, params.y);
  },
});

const mouseDown = defineTabTool({
  capability: 'vision',

  schema: {
    name: 'browser_mouse_down',
    title: 'Press mouse down',
    description: 'Press mouse down',
    inputSchema: z.object({
      button: z.enum(['left', 'right', 'middle']).optional().describe('Button to press, defaults to left'),
    }),
    type: 'input',
  },

  handle: async (tab, params, response) => {
    const options = { button: params.button };
    const optionsArg = formatObjectOrVoid(options);

    response.addCode(`// Press mouse down`);
    response.addCode(`await page.mouse.down(${optionsArg});`);
    await tab.page.mouse.down(options);
  },
});

const mouseUp = defineTabTool({
  capability: 'vision',

  schema: {
    name: 'browser_mouse_up',
    title: 'Press mouse up',
    description: 'Press mouse up',
    inputSchema: z.object({
      button: z.enum(['left', 'right', 'middle']).optional().describe('Button to press, defaults to left'),
    }),
    type: 'input',
  },

  handle: async (tab, params, response) => {
    const options = { button: params.button };
    const optionsArg = formatObjectOrVoid(options);

    response.addCode(`// Press mouse up`);
    response.addCode(`await page.mouse.up(${optionsArg});`);
    await tab.page.mouse.up(options);
  },
});

const mouseWheel = defineTabTool({
  capability: 'vision',
  schema: {
    name: 'browser_mouse_wheel',
    title: 'Scroll mouse wheel',
    description: 'Scroll mouse wheel',
    inputSchema: z.object({
      deltaX: z.number().default(0).describe('X delta'),
      deltaY: z.number().default(0).describe('Y delta'),
    }),
    type: 'input',
  },

  handle: async (tab, params, response) => {
    response.addCode(`// Scroll mouse wheel`);
    response.addCode(`await page.mouse.wheel(${params.deltaX}, ${params.deltaY});`);
    await tab.page.mouse.wheel(params.deltaX, params.deltaY);
  },
});

const mouseClick = defineTabTool({
  capability: 'vision',
  schema: {
    name: 'browser_mouse_click_xy',
    title: 'Click',
    description: 'Click mouse button at a given position',
    inputSchema: z.object({
      x: z.number().describe('X coordinate'),
      y: z.number().describe('Y coordinate'),
      button: z.enum(['left', 'right', 'middle']).optional().describe('Button to click, defaults to left'),
      clickCount: z.number().optional().describe('Number of clicks, defaults to 1'),
      delay: z.number().optional().describe('Time to wait between mouse down and mouse up in milliseconds, defaults to 0'),
    }),
    type: 'input',
  },

  handle: async (tab, params, response) => {
    response.setIncludeSnapshot();

    const options = {
      button: params.button,
      clickCount: params.clickCount,
      delay: params.delay,
    };
    const formatted = formatObjectOrVoid(options);
    const optionsArg = formatted ? `, ${formatted}` : '';

    response.addCode(`// Click mouse at coordinates (${params.x}, ${params.y})`);
    response.addCode(`await page.mouse.click(${params.x}, ${params.y}${optionsArg});`);

    await tab.waitForCompletion(async () => {
      await tab.page.mouse.click(params.x, params.y, options);
    });
  },
});

const mouseDrag = defineTabTool({
  capability: 'vision',
  schema: {
    name: 'browser_mouse_drag_xy',
    title: 'Drag mouse',
    description: 'Drag left mouse button to a given position',
    inputSchema: z.object({
      startX: z.number().describe('Start X coordinate'),
      startY: z.number().describe('Start Y coordinate'),
      endX: z.number().describe('End X coordinate'),
      endY: z.number().describe('End Y coordinate'),
    }),
    type: 'input',
  },

  handle: async (tab, params, response) => {
    response.setIncludeSnapshot();

    response.addCode(`// Drag mouse from (${params.startX}, ${params.startY}) to (${params.endX}, ${params.endY})`);
    response.addCode(`await page.mouse.move(${params.startX}, ${params.startY});`);
    response.addCode(`await page.mouse.down();`);
    response.addCode(`await page.mouse.move(${params.endX}, ${params.endY});`);
    response.addCode(`await page.mouse.up();`);

    await tab.waitForCompletion(async () => {
      await tab.page.mouse.move(params.startX, params.startY);
      await tab.page.mouse.down();
      await tab.page.mouse.move(params.endX, params.endY);
      await tab.page.mouse.up();
    });
  },
});

export default [
  mouseMove,
  mouseClick,
  mouseDrag,
  mouseDown,
  mouseUp,
  mouseWheel,
];
