import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'

import type { Page } from '@playwright/test'
import { expect } from '@playwright/test'

import { comfyPageFixture as test } from '@e2e/fixtures/ComfyPage'

const TEST_PRESET = {
  name: 'test-preset',
  newBindings: [
    {
      commandId: 'Comfy.Canvas.SelectAll',
      combo: { key: 'a', ctrl: true, shift: true },
      targetElementId: 'graph-canvas-container'
    }
  ],
  unsetBindings: [
    {
      commandId: 'Comfy.Canvas.SelectAll',
      combo: { key: 'a', ctrl: true },
      targetElementId: 'graph-canvas-container'
    }
  ]
}

async function importPreset(page: Page, preset: typeof TEST_PRESET) {
  const menuButton = page.getByTestId('keybinding-preset-menu')
  await menuButton.click()

  const fileChooserPromise = page.waitForEvent('filechooser')
  await page.getByRole('menuitem', { name: /Import preset/i }).click()
  const fileChooser = await fileChooserPromise

  const presetPath = path.join(os.tmpdir(), 'test-preset.json')
  fs.writeFileSync(presetPath, JSON.stringify(preset))
  await fileChooser.setFiles(presetPath)
}

test.beforeEach(async ({ comfyPage }) => {
  await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Disabled')
})

test.afterEach(async ({ comfyPage }) => {
  await comfyPage.request.fetch(
    `${comfyPage.url}/api/userdata/keybindings%2Ftest-preset.json`,
    { method: 'DELETE' }
  )
  await comfyPage.settings.setSetting(
    'Comfy.Keybinding.CurrentPreset',
    'default'
  )
})

test.describe('Keybinding Presets', { tag: '@keyboard' }, () => {
  test('Can import a preset, use remapped keybinding, and switch back to default', async ({
    comfyPage
  }) => {
    test.setTimeout(30000)
    const { page } = comfyPage

    // Verify default Ctrl+A select-all works
    await comfyPage.workflow.loadWorkflow('default')
    await comfyPage.canvas.press('Control+a')
    await comfyPage.canvas.press('Delete')
    await expect.poll(() => comfyPage.nodeOps.getGraphNodesCount()).toBe(0)

    // Open keybinding settings panel
    await comfyPage.settingDialog.open()
    await comfyPage.settingDialog.category('Keybinding').click()

    await importPreset(page, TEST_PRESET)

    // Verify active preset switched to test-preset
    const presetTrigger = page
      .locator('#keybinding-panel-actions')
      .locator('button[role="combobox"]')
    await expect(presetTrigger).toContainText('test-preset')

    // Wait for toast to auto-dismiss, then close settings via Escape
    await expect(comfyPage.toast.visibleToasts).toHaveCount(0)
    await page.keyboard.press('Escape')
    await comfyPage.settingDialog.waitForHidden()

    // Load workflow again, use new keybind Ctrl+Shift+A
    await comfyPage.workflow.loadWorkflow('default')
    await comfyPage.canvas.press('Control+Shift+a')
    await expect
      .poll(() => comfyPage.nodeOps.getSelectedGraphNodesCount())
      .toBeGreaterThan(0)
    await comfyPage.canvas.press('Delete')
    await expect.poll(() => comfyPage.nodeOps.getGraphNodesCount()).toBe(0)

    // Switch back to default preset
    await comfyPage.settingDialog.open()
    await comfyPage.settingDialog.category('Keybinding').click()

    await presetTrigger.click()
    await page.getByRole('option', { name: /Default Preset/i }).click()

    // Handle unsaved changes dialog if the preset was marked as modified
    const discardButton = page.getByRole('button', {
      name: /Discard and Switch/i
    })
    if (await discardButton.isVisible({ timeout: 2000 }).catch(() => false)) {
      await discardButton.click()
    }

    await expect(presetTrigger).toContainText('Default Preset')

    await page.keyboard.press('Escape')
    await comfyPage.settingDialog.waitForHidden()
  })

  test('Can export a preset and re-import it', async ({ comfyPage }) => {
    test.setTimeout(30000)
    const { page } = comfyPage
    const menuButton = page.getByTestId('keybinding-preset-menu')

    // Open keybinding settings panel
    await comfyPage.settingDialog.open()
    await comfyPage.settingDialog.category('Keybinding').click()

    await importPreset(page, TEST_PRESET)

    // Verify active preset switched to test-preset
    const presetTrigger = page
      .locator('#keybinding-panel-actions')
      .locator('button[role="combobox"]')
    await expect(presetTrigger).toContainText('test-preset')

    // Wait for toast to auto-dismiss
    await expect(comfyPage.toast.visibleToasts).toHaveCount(0)

    // Export via ellipsis menu
    await menuButton.click()
    const downloadPromise = page.waitForEvent('download')
    await page.getByRole('menuitem', { name: /Export preset/i }).click()
    const download = await downloadPromise

    // Verify filename contains test-preset
    expect(download.suggestedFilename()).toContain('test-preset')

    // Close settings
    await page.keyboard.press('Escape')
    await comfyPage.settingDialog.waitForHidden()

    // Verify the downloaded file is valid JSON with correct structure
    const downloadPath = await download.path()
    expect(downloadPath).toBeTruthy()
    const content = fs.readFileSync(downloadPath!, 'utf-8')
    const parsed = JSON.parse(content) as {
      name: string
      newBindings: unknown[]
      unsetBindings: unknown[]
    }
    expect(parsed).toHaveProperty('name')
    expect(parsed).toHaveProperty('newBindings')
    expect(parsed).toHaveProperty('unsetBindings')
    expect(parsed.name).toBe('test-preset')
  })

  test('Can delete an imported preset', async ({ comfyPage }) => {
    test.setTimeout(30000)
    const { page } = comfyPage
    const menuButton = page.getByTestId('keybinding-preset-menu')

    // Open keybinding settings panel
    await comfyPage.settingDialog.open()
    await comfyPage.settingDialog.category('Keybinding').click()

    await importPreset(page, TEST_PRESET)

    // Verify active preset switched to test-preset
    const presetTrigger = page
      .locator('#keybinding-panel-actions')
      .locator('button[role="combobox"]')
    await expect(presetTrigger).toContainText('test-preset')

    // Wait for toast to auto-dismiss
    await expect(comfyPage.toast.visibleToasts).toHaveCount(0)

    // Delete via ellipsis menu
    await menuButton.click()
    await page.getByRole('menuitem', { name: /Delete preset/i }).click()

    // Confirm deletion in the dialog
    const confirmDialog = page.getByRole('dialog', {
      name: /Delete the current preset/i
    })
    await confirmDialog.getByRole('button', { name: /Delete/i }).click()

    // Verify preset trigger now shows Default Preset
    await expect(presetTrigger).toContainText('Default Preset')

    // Close settings
    await page.keyboard.press('Escape')
    await comfyPage.settingDialog.waitForHidden()
  })

  test('Can save modifications as a new preset', async ({ comfyPage }) => {
    test.setTimeout(30000)
    const { page } = comfyPage
    const menuButton = page.getByTestId('keybinding-preset-menu')

    // Open keybinding settings panel
    await comfyPage.settingDialog.open()
    await comfyPage.settingDialog.category('Keybinding').click()

    await importPreset(page, TEST_PRESET)

    // Verify active preset switched to test-preset
    const presetTrigger = page
      .locator('#keybinding-panel-actions')
      .locator('button[role="combobox"]')
    await expect(presetTrigger).toContainText('test-preset')

    // Wait for toast to auto-dismiss
    await expect(comfyPage.toast.visibleToasts).toHaveCount(0)

    // Save as new preset via ellipsis menu
    await menuButton.click()
    await page.getByRole('menuitem', { name: /Save as new preset/i }).click()

    // Fill in the preset name in the prompt dialog
    const promptInput = page.locator('.prompt-dialog-content input')
    await promptInput.fill('my-custom-preset')
    await promptInput.press('Enter')

    // Wait for toast to auto-dismiss
    await expect(comfyPage.toast.visibleToasts).toHaveCount(0)

    // Verify preset trigger shows my-custom-preset
    await expect(presetTrigger).toContainText('my-custom-preset')

    // Close settings
    await page.keyboard.press('Escape')
    await comfyPage.settingDialog.waitForHidden()

    // Cleanup: delete the my-custom-preset file
    await comfyPage.request.fetch(
      `${comfyPage.url}/api/userdata/keybindings%2Fmy-custom-preset.json`,
      { method: 'DELETE' }
    )
  })
})
