import { By } from 'selenium-webdriver'
import fs from 'fs'
import { expect } from 'chai'
import { Condition } from 'selenium-webdriver'

export const DEFAULT_UI_WAIT_MS = 3000

// Keep Selenium helpers in lockstep with the frontend DOM id helpers.
export {
  ARGUMENT_FIELD_ID_PREFIX,
  argumentFieldChoicesId,
  argumentFieldId,
  argumentFieldValidationElementId,
  argumentFieldValueId
} from '../../frontend/resources/vue/utils/argumentFieldIds.js'

const executionDialogStatusBy = By.css('.execution-dialog-status')

export async function getActionButtons () {
  // Currently, only the active dashboard's contents are rendered,
  // so we don't need to scope the selector by dashboard title.
  return await webdriver.findElements(By.css('.action-button button'))
}

export async function getExecutionDialogOutput() {
    await webdriver.wait(new Condition('Dialog with long int is visible', async () => {
      const dialog = await webdriver.findElement({ id: 'execution-results-popup' })
      return await dialog.isDisplayed()
    }));

    const ret = await webdriver.executeScript('return window.logEntries.get(window.executionDialog.executionTrackingId).output')

    return ret
}

export async function closeExecutionDialog() {
    const btnClose = await webdriver.findElements(By.css('[title="Close"]'))
    await btnClose[0].click()
}

export function takeScreenshotOnFailure (test, webdriver) {
    if (test.state === 'failed') {
      const title = test.fullTitle();

      console.log(`Test failed, taking screenshot: ${title}`);
      takeScreenshot(webdriver, title);
    }
}

export function takeScreenshot (webdriver, title) {
  return webdriver.takeScreenshot().then((img) => {
    fs.mkdirSync('screenshots', { recursive: true });

  title = title.replaceAll('config: ', '')
	title = title.replaceAll(/[\(\)\|\*\<\>\:]/g, "_")
	title = title + '.failed-test'

    fs.writeFileSync('screenshots/' + title + '.png', img, 'base64')
  })
}

export async function waitForDashboardLoaded(timeoutMs = DEFAULT_UI_WAIT_MS, expectedTitle = null) {
  await webdriver.wait(new Condition('wait for loaded-dashboard', async function () {
    const body = await webdriver.findElement(By.tagName('body'))
    const attr = await body.getAttribute('loaded-dashboard')

    console.log('loaded-dashboard: ', attr)

    if (attr == null || attr === '') {
      return false
    }

    if (expectedTitle != null) {
      return attr === expectedTitle
    }

    return true
  }), timeoutMs)
}

export async function waitForLogsPage(timeoutMs = DEFAULT_UI_WAIT_MS) {
  await webdriver.wait(new Condition('wait for logs page', async () => {
    const url = await webdriver.getCurrentUrl()
    return url.includes('/logs/') && !url.endsWith('/logs')
  }), timeoutMs)
}

export async function waitForArgumentFormPage(timeoutMs = DEFAULT_UI_WAIT_MS) {
  await webdriver.wait(new Condition('wait for argument form page', async () => {
    const url = await webdriver.getCurrentUrl()
    return url.includes('/actionBinding/') && url.includes('/argumentForm')
  }), timeoutMs)
}

export async function waitForArgumentFormReady(timeoutMs = DEFAULT_UI_WAIT_MS) {
  await webdriver.wait(new Condition('wait for argument form ready', async () => {
    const body = await webdriver.findElement(By.tagName('body'))
    const attr = await body.getAttribute('loaded-argument-form')
    return attr != null && attr !== ''
  }), timeoutMs)
}

export async function waitForExecutionComplete(timeoutMs = DEFAULT_UI_WAIT_MS) {
  await webdriver.wait(new Condition('wait for execution status', async () => {
    const statusElements = await webdriver.findElements(executionDialogStatusBy)
    return statusElements.length > 0
  }), timeoutMs)

  await webdriver.wait(new Condition('wait for execution to finish', async () => {
    try {
      const statusElement = await webdriver.findElement(executionDialogStatusBy)
      const statusText = await statusElement.getText()
      return !statusText.includes('Still running') && !statusText.includes('Queued')
    } catch (e) {
      return false
    }
  }), timeoutMs)
}

export async function getRootAndWait() {
  await webdriver.get(runner.baseUrl())
  await waitForDashboardLoaded()
}

export async function closeSidebar() {
  await webdriver.findElement(By.id('sidebar-toggler-button')).click()

  const sidebar = await webdriver.findElement(By.id('mainnav'))

  const neededLeft = '-250px' // Assuming sidebar is closed at this position

  let lastLeft = ''

  await webdriver.wait(new Condition('wait for sidebar to close', async function() {
    const left = await sidebar.getCssValue('left')

    if (left !== lastLeft) {
      lastLeft = left
      console.log('Sidebar left changed to: ', left)
      return false
    } else {
      console.log('Sidebar closed, left is: *' + left, left === neededLeft ? ' (as expected)' : '')
      return left === neededLeft
    }
  }), DEFAULT_UI_WAIT_MS)
}

export async function openSidebar() {
  await webdriver.findElement(By.id('sidebar-toggler-button')).click()

  const sidebar = await webdriver.findElement(By.id('mainnav'))

  let lastLeft = 0

  await webdriver.wait(new Condition('wait for sidebar to open', async function() {
    const left = await sidebar.getCssValue('left')

    if (left !== lastLeft) {
      lastLeft = left
      console.log('Sidebar left changed to: ', left)
      return false
    } else {
      console.log('Sidebar opened, left is: ', left)
      return true
    }
  }), DEFAULT_UI_WAIT_MS)
}

export async function getNavigationLinks() {
  const navigationLinks = await webdriver.findElements(By.css('.navigation-links li'))

  return navigationLinks
}

export async function requireExecutionDialogStatus (webdriver, expected) {
  await webdriver.wait(new Condition('wait for action to be running', async function () {
    const dialogStatus = await webdriver.findElement(executionDialogStatusBy)
    const actual = await dialogStatus.getText()

    if (actual === expected) {
      return true
    } else {
      console.log('Waiting for domStatus text to be: ', expected, ', it is currently: ', actual)
      return false
    }
  }), DEFAULT_UI_WAIT_MS)
}

export async function findExecutionDialog (webdriver) {
  return webdriver.findElement(By.id('execution-results-popup'))
}

export async function getActionButton (webdriver, title) {
  const buttons = await webdriver.findElements(By.css('[title="' + title + '"]'))

  expect(buttons).to.have.length(1)

  return buttons[0]
}

export async function getTerminalBuffer() {
  try {
    const output = await webdriver.executeScript(`
      if (window.terminal && window.terminal.getBufferAsString) {
        return window.terminal.getBufferAsString();
      }
      return null;
    `)
    return output
  } catch (e) {
    console.log('[getTerminalBuffer] Error:', e.message)
    return null
  }
}
