import { Locator, Page } from '@playwright/test'

type Coordinate = {
  top: number
  left: number
}

/**
 * Checks if an element with id ` playwright-mousepointer` exists in the page and returns it.
 * If it does not exist, it creates a new element in the center of the page and returns it.
 */
async function getFakeMousePointerElement(page: Page) {
  const fakeMousePointerElement = await page.$('#playwright-mousepointer')
  if (fakeMousePointerElement) {
    return fakeMousePointerElement
  }
  return page.evaluateHandle(() => {
    const div = document.createElement('div')
    div.id = 'playwright-mousepointer'
    div.style.position = 'fixed'
    div.style.top = '0px'
    div.style.left = '0px'
    div.style.width = '15px'
    div.style.height = '15px'
    div.style.borderRadius = '50%'
    div.style.backgroundColor = 'blue'
    div.style.border = '2px solid white'
    div.style.boxShadow = '0 0 5px rgba(0, 0, 0, 0.5)'
    div.style.zIndex = '999999'
    div.style.pointerEvents = 'none' // Ensures the fake pointer doesn't interfere with real pointer events
    document.body.appendChild(div)
    return div
  })
}

/**
 * Moves a fake mouse pointer to the specified item with an offset and adds scaling animations.
 *
 * @example
 * const item = page.locator('.some-class');
 * await moveMouse(item, { top: 10, left: 30 });
 */
export async function moveMouse(item: Locator, offset: Coordinate = { top: 0, left: 20 }) {
  const firstItem = item.nth(0)
  if (!firstItem) {
    return
  }
  const boundingBox = await firstItem.boundingBox()
  if (!boundingBox) {
    return
  }

  const targetPostion = { left: boundingBox.x + offset.left, top: boundingBox.y + boundingBox.height / 2 + offset.top }

  const page = item.page()
  const mousePointer = await getFakeMousePointerElement(page)

  //add animation that moves the mousepointer to the new position
  await mousePointer.evaluate((element: HTMLElement, { left, top }) => {
    element.style.transition = 'transform 1.0s'
    element.style.transform = `translate(${left}px, ${top}px)`
  }, targetPostion)

  await page.waitForTimeout(1200)

  await mousePointer.evaluate((element: HTMLElement) => {
    const scaleTransform = `scale(0.2)`
    let transform = element.style.transform
    if (transform.includes('scale')) {
      transform = transform.replace(/scale\([0-9.]+\)/, scaleTransform)
    }
    transform = `${transform} ${scaleTransform}`

    element.style.transition = 'transform 0.2s'
    element.style.transform = transform
  })
  await page.waitForTimeout(200)

  await mousePointer.evaluate((element: HTMLElement) => {
    const scaleTransform = `scale(1.0)`
    let transform = element.style.transform
    if (transform.includes('scale')) {
      transform = transform.replace(/scale\([0-9.]+\)/, scaleTransform)
    }
    transform = `${transform} ${scaleTransform}`

    element.style.transition = 'transform 0.2s'
    element.style.transform = transform
  })
  await page.waitForTimeout(200)
}

/**
 * Displays an annotation message on the given page.
 *
 * @example
 * const page = await browser.newPage();
 * await annotate(page, 'This is an annotation message.');
 */
export async function annotate(page: Page, message: string) {
  const annotationElement = await getAnnotationElement(page)

  return annotationElement.evaluate((element, message) => {
    element.style.display = 'block'
    element.textContent = message
  }, message)
}

// AnnotationElement is a div at the bottom of the screen that displays the message
// The messsage is on top of everyhting else and a box with white background, gray border and black text
// The text is centered and the height of the div changes based on the length of the message - while the width is constant 1000px
// The method checks if an element with the id "annotation" exists and returns it - else it creates it

/**
 * Retrieves the annotation element from the page. If it doesn't exist, creates and returns a new annotation element.
 *
 * @example
 * const page = await browser.newPage();
 * const annotationElement = await getAnnotationElement(page);
 */

async function getAnnotationElement(page: Page) {
  const annotationElement = await page.$('#playwright-annotation')
  if (annotationElement) {
    return annotationElement
  }
  return page.evaluateHandle(() => {
    const annotationElement = document.createElement('div')
    annotationElement.id = 'playwright-annotation'
    annotationElement.style.position = 'fixed'
    annotationElement.style.bottom = '5%'
    annotationElement.style.left = '20%'
    annotationElement.style.width = '60%'
    annotationElement.style.height = 'auto'
    annotationElement.style.backgroundColor = 'rgba(250, 220, 217, 0.9)'
    annotationElement.style.border = '1px solid gray'
    annotationElement.style.color = 'black'
    annotationElement.style.textAlign = 'center'
    annotationElement.style.zIndex = '999999'
    annotationElement.style.pointerEvents = 'none'
    annotationElement.style.fontSize = '24px'
    annotationElement.style.fontWeight = 'bold'
    annotationElement.style.padding = '20px'
    document.body.appendChild(annotationElement)
    return annotationElement
  })
}

/**
 * Hides the annotation element on the given page.
 *
 * @example
 * const page = await browser.newPage();
 * await hideAnnotation(page);
 */

export async function hideAnnotation(page: Page) {
  const annotationElement = await getAnnotationElement(page)
  return annotationElement.evaluate((element) => {
    element.style.display = 'none'
  })
}
