import { type Page, type Locator, expect } from '@playwright/test'
import type { MindElixirCtor, MindElixirData, MindElixirInstance, Options } from '../src'
import type MindElixir from '../src'
interface Window {
  m: MindElixirInstance
  MindElixir: MindElixirCtor
  E: typeof MindElixir.E
}
declare let window: Window

export class MindElixirFixture {
  private m: MindElixirInstance

  constructor(public readonly page: Page) {
    //
  }

  async goto() {
    await this.page.goto('http://localhost:23334/test.html')
  }
  async init(data: MindElixirData, el = '#map') {
    // evaluate return Serializable value
    await this.page.evaluate(
      ({ data, el }) => {
        const MindElixir = window.MindElixir
        const options: Options = {
          el,
          direction: MindElixir.SIDE,
          allowUndo: true, // Enable undo/redo functionality for tests
          keypress: true, // Enable keyboard shortcuts
          editable: true, // Enable editing
        }
        const mind = new MindElixir(options)
        mind.init(JSON.parse(JSON.stringify(data)))
        window[el] = mind
        return mind
      },
      { data, el }
    )
  }
  async getInstance(el = '#map') {
    const instanceHandle = await this.page.evaluateHandle(el => Promise.resolve(window[el] as MindElixirInstance), el)
    return instanceHandle
  }
  async getData(el = '#map') {
    const data = await this.page.evaluate(el => {
      return window[el].getData()
    }, el)
    // console.log(a)
    // const dataHandle = await this.page.evaluateHandle(() => Promise.resolve(window.m.getData()))
    // const data = await dataHandle.jsonValue()
    return data
  }
  async dblclick(topic: string) {
    await this.page.getByText(topic, { exact: true }).dblclick({
      force: true,
    })
  }
  async click(topic: string) {
    await this.page.getByText(topic, { exact: true }).click({
      force: true,
    })
  }
  getByText(topic: string) {
    return this.page.getByText(topic, { exact: true })
  }
  async dragOver(topic: string, type: 'before' | 'after' | 'in') {
    await this.page.getByText(topic).hover({ force: true })
    await this.page.mouse.down()
    const target = await this.page.getByText(topic)
    const box = (await target.boundingBox())!
    const y = type === 'before' ? -12 : type === 'after' ? box.height + 12 : box.height / 2
    // https://playwright.dev/docs/input#dragging-manually
    // If your page relies on the dragover event being dispatched, you need at least two mouse moves to trigger it in all browsers.
    await this.page.mouse.move(box.x + box.width / 2, box.y + y)
    await this.page.waitForTimeout(100) // throttle
    await this.page.mouse.move(box.x + box.width / 2, box.y + y)
  }
  async dragSelect(topic1: string, topic2: string) {
    // Get the bounding boxes for both topics
    const element1 = this.page.getByText(topic1, { exact: true })
    const element2 = this.page.getByText(topic2, { exact: true })

    const box1 = await element1.boundingBox()
    const box2 = await element2.boundingBox()

    if (!box1 || !box2) {
      throw new Error(`Could not find bounding box for topics: ${topic1}, ${topic2}`)
    }

    // Calculate the selection area coordinates
    // Find the minimum and maximum x, y coordinates
    const minX = Math.min(box1.x, box2.x) - 10
    const minY = Math.min(box1.y, box2.y) - 10
    const maxX = Math.max(box1.x + box1.width, box2.x + box2.width) + 10
    const maxY = Math.max(box1.y + box1.height, box2.y + box2.height) + 10

    // Perform the drag selection
    await this.page.mouse.move(minX, minY)
    await this.page.mouse.down()
    await this.page.waitForTimeout(100) // throttle
    await this.page.mouse.move(maxX, maxY)
    await this.page.mouse.up()
  }
  async toHaveScreenshot(locator?: Locator) {
    await expect(locator || this.page.locator('me-nodes')).toHaveScreenshot({
      maxDiffPixelRatio: 0.02,
    })
  }
}
