import type { BrowserContext, Page } from '@playwright/test'

import { expect, test } from '@playwright/test'
import path from 'path'
import { fileURLToPath } from 'url'

import type { PayloadTestSDK } from '../../../helpers/sdk/index.js'
import type { Config } from '../../payload-types.js'

import {
  ensureCompilationIsDone,
  initPageConsoleErrorCatch,
  saveDocAndAssert,
  // throttleTest,
} from '../../../helpers.js'
import { AdminUrlUtil } from '../../../helpers/adminUrlUtil.js'
import { initPayloadE2ENoConfig } from '../../../helpers/initPayloadE2ENoConfig.js'
import { reInitializeDB } from '../../../helpers/reInitializeDB.js'
import { RESTClient } from '../../../helpers/rest.js'
import { TEST_TIMEOUT_LONG } from '../../../playwright.config.js'
import { conditionalLogicSlug } from '../../slugs.js'

const filename = fileURLToPath(import.meta.url)
const currentFolder = path.dirname(filename)
const dirname = path.resolve(currentFolder, '../../')

const { beforeAll, beforeEach, describe } = test

let payload: PayloadTestSDK<Config>
let client: RESTClient
let page: Page
let serverURL: string
let url: AdminUrlUtil
let context: BrowserContext

const toggleConditionAndCheckField = async (toggleLocator: string, fieldLocator: string) => {
  const toggle = page.locator(toggleLocator)

  if (!(await toggle.isChecked())) {
    await expect(page.locator(fieldLocator)).toBeHidden()
    await toggle.click()
    await expect(page.locator(fieldLocator)).toBeVisible()
  } else {
    await expect(page.locator(fieldLocator)).toBeVisible()
    await toggle.click()
    await expect(page.locator(fieldLocator)).toBeHidden()
  }
}

describe('Conditional Logic', () => {
  beforeAll(async ({ browser }, testInfo) => {
    testInfo.setTimeout(TEST_TIMEOUT_LONG)
    process.env.SEED_IN_CONFIG_ONINIT = 'false' // Makes it so the payload config onInit seed is not run. Otherwise, the seed would be run unnecessarily twice for the initial test run - once for beforeEach and once for onInit
    ;({ payload, serverURL } = await initPayloadE2ENoConfig<Config>({
      dirname,
      // prebuild,
    }))

    url = new AdminUrlUtil(serverURL, conditionalLogicSlug)

    context = await browser.newContext()
    page = await context.newPage()
    initPageConsoleErrorCatch(page)

    await ensureCompilationIsDone({ page, serverURL })
  })

  beforeEach(async () => {
    // await throttleTest({
    //   page,
    //   context,
    //   delay: 'Fast 4G',
    // })

    await reInitializeDB({
      serverURL,
      snapshotKey: 'fieldsTest',
      uploadsDir: path.resolve(dirname, './collections/Upload/uploads'),
    })

    if (client) {
      await client.logout()
    }

    client = new RESTClient({ defaultSlug: 'users', serverURL })
    await client.login()
    await ensureCompilationIsDone({ page, serverURL })
  })

  test("should conditionally render field based on another field's data", async () => {
    await page.goto(url.create)

    await toggleConditionAndCheckField(
      'label[for=field-toggleField]',
      'label[for=field-fieldWithCondition]',
    )

    expect(true).toBe(true)
  })

  test('should conditionally render custom field that renders a Payload field', async () => {
    await page.goto(url.create)

    await toggleConditionAndCheckField(
      'label[for=field-toggleField]',
      'label[for=field-customFieldWithField]',
    )

    expect(true).toBe(true)
  })

  test('should conditionally render custom field that wraps itself with the withCondition HOC (legacy)', async () => {
    await page.goto(url.create)

    await toggleConditionAndCheckField(
      'label[for=field-toggleField]',
      'label[for=field-customFieldWithHOC]',
    )

    expect(true).toBe(true)
  })

  test('should toggle conditional custom client field', async () => {
    await page.goto(url.create)
    await toggleConditionAndCheckField('label[for=field-toggleField]', '#custom-client-field')
    expect(true).toBe(true)
  })

  test('should conditionally render custom server field', async () => {
    await page.goto(url.create)
    await toggleConditionAndCheckField('label[for=field-toggleField]', '#custom-server-field')
    expect(true).toBe(true)
  })

  test('should conditionally render rich text fields', async () => {
    await page.goto(url.create)
    await toggleConditionAndCheckField(
      'label[for=field-toggleField]',
      '.field-type.rich-text-lexical',
    )
    expect(true).toBe(true)
  })

  test('should show conditional field based on user data', async () => {
    await page.goto(url.create)
    const userConditional = page.locator('input#field-userConditional')
    await expect(userConditional).toBeVisible()
  })

  test('should show conditional field based on nested field data', async () => {
    await page.goto(url.create)

    const parentGroupFields = page.locator(
      'div#field-parentGroup > .group-field__wrap > .render-fields',
    )
    await expect(parentGroupFields).toHaveCount(1)

    const toggle = page.locator('label[for=field-parentGroup__enableParentGroupFields]')
    await toggle.click()

    const toggledField = page.locator('input#field-parentGroup__siblingField')

    await expect(toggledField).toBeVisible()
  })

  test('should show conditional field based on siblingData', async () => {
    await page.goto(url.create)

    const toggle = page.locator('label[for=field-parentGroup__enableParentGroupFields]')
    await toggle.click()

    const fieldRelyingOnSiblingData = page.locator('input#field-reliesOnParentGroup')
    await expect(fieldRelyingOnSiblingData).toBeVisible()
  })

  test('should not render fields when adding array or blocks rows until form state returns', async () => {
    await page.goto(url.create)
    await page.locator('#field-arrayWithConditionalField .array-field__add-row').click()
    const shimmer = '#field-arrayWithConditionalField .collapsible__content > .shimmer-effect'

    await expect(page.locator(shimmer)).toBeVisible()

    await expect(page.locator(shimmer)).toBeHidden()

    // Do not use `waitForSelector` here, as it will wait for the selector to appear, not disappear
    // eslint-disable-next-line playwright/no-wait-for-selector
    const wasFieldAttached = await page
      .waitForSelector('input#field-arrayWithConditionalField__0__textWithCondition', {
        state: 'attached',
        timeout: 100, // A small timeout to catch any transient rendering
      })
      .catch(() => false) // If it doesn't appear, this resolves to `false`

    expect(wasFieldAttached).toBeFalsy()

    const fieldToToggle = page.locator('input#field-enableConditionalFields')
    await fieldToToggle.click()

    await expect(
      page.locator('input#field-arrayWithConditionalField__0__textWithCondition'),
    ).toBeVisible()
  })

  test('should render field based on path argument', async () => {
    await page.goto(url.create)

    const arrayOneButton = page.locator('#field-arrayOne .array-field__add-row')
    await arrayOneButton.click()

    const arrayTwoButton = page.locator('#arrayOne-row-0 .array-field__add-row')
    await arrayTwoButton.click()

    const arrayThreeButton = page.locator('#arrayOne-0-arrayTwo-row-0 .array-field__add-row')
    await arrayThreeButton.click()

    const numberField = page.locator('#field-arrayOne__0__arrayTwo__0__arrayThree__0__numberField')

    await expect(numberField).toBeHidden()

    const selectField = page.locator('#field-arrayOne__0__arrayTwo__0__selectOptions')

    await selectField.click({ delay: 100 })
    const options = page.locator('.rs__option')

    await options.locator('text=Option Two').click()

    await expect(numberField).toBeVisible()
  })

  test('should render field based on operation argument', async () => {
    await page.goto(url.create)

    const textField = page.locator('#field-text')
    const fieldWithOperationCondition = page.locator('#field-fieldWithOperationCondition')

    await textField.fill('some text')

    await expect(fieldWithOperationCondition).toBeVisible()

    await saveDocAndAssert(page)

    await expect(fieldWithOperationCondition).toBeHidden()
  })
})
