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

const BACKEND = 'http://localhost:5000'

const CHILD_NAME = 'DialogHelpChild'
const CHORE_NAME = 'DialogHelpChore'
const KINDNESS_NAME = 'DialogHelpKindness'
const PENALTY_NAME = 'DialogHelpPenalty'
const REWARD_NAME = 'DialogHelpReward'
const REWARD_COST = 10
const ROUTINE_NAME = 'DialogHelpRoutine'

async function setTutorialEnabled(request: APIRequestContext, enabled: boolean): Promise<void> {
  const res = await request.patch(`${BACKEND}/user/tutorial-progress`, {
    data: { enabled },
  })
  if (!res.ok()) {
    throw new Error(
      `Failed to set tutorial enabled=${enabled}: ${res.status()} ${await res.text()}`,
    )
  }
}

async function resetTutorialProgress(request: APIRequestContext): Promise<void> {
  const res = await request.patch(`${BACKEND}/user/tutorial-progress`, {
    data: { reset: true },
  })
  if (!res.ok()) {
    throw new Error(`Failed to reset tutorial progress: ${res.status()} ${await res.text()}`)
  }
}

async function createChild(request: APIRequestContext, name: string): Promise<string> {
  const pre = await request.get(`${BACKEND}/child/list`)
  for (const c of (await pre.json()).children ?? []) {
    if (c.name === name) await request.delete(`${BACKEND}/child/${c.id}`)
  }
  await request.put(`${BACKEND}/child/add`, { data: { name, age: 8 } })
  const list = await request.get(`${BACKEND}/child/list`)
  return (
    (await list.json()).children?.find(
      (c: { name: string; id: string }) => c.name === name,
    )?.id ?? ''
  )
}

async function createTask(
  request: APIRequestContext,
  name: string,
  type: 'chore' | 'kindness' | 'penalty',
): Promise<string> {
  const pre = await request.get(`${BACKEND}/task/list`)
  for (const t of (await pre.json()).tasks ?? []) {
    if (t.name === name) await request.delete(`${BACKEND}/task/${t.id}`)
  }
  await request.put(`${BACKEND}/task/add`, { data: { name, points: 5, type } })
  const list = await request.get(`${BACKEND}/task/list`)
  return (
    (await list.json()).tasks?.find((t: { name: string; id: string }) => t.name === name)?.id ?? ''
  )
}

async function createReward(request: APIRequestContext, name: string): Promise<string> {
  const pre = await request.get(`${BACKEND}/reward/list`)
  for (const r of (await pre.json()).rewards ?? []) {
    if (r.name === name) await request.delete(`${BACKEND}/reward/${r.id}`)
  }
  await request.put(`${BACKEND}/reward/add`, {
    data: { name, description: 'E2E dialog reward', cost: REWARD_COST },
  })
  const list = await request.get(`${BACKEND}/reward/list`)
  return (
    (await list.json()).rewards?.find((r: { name: string; id: string }) => r.name === name)?.id ??
    ''
  )
}

async function createRoutine(request: APIRequestContext, name: string): Promise<string> {
  const pre = await request.get(`${BACKEND}/routine/list`)
  for (const r of (await pre.json()).routines ?? []) {
    if (r.name === name) await request.delete(`${BACKEND}/routine/${r.id}`)
  }
  const res = await request.put(`${BACKEND}/routine/add`, { data: { name, points: 5 } })
  return (await res.json()).routine?.id ?? ''
}

async function assignTask(
  request: APIRequestContext,
  childId: string,
  taskId: string,
  type: 'chore' | 'kindness' | 'penalty',
): Promise<void> {
  const res = await request.put(`${BACKEND}/child/${childId}/set-tasks`, {
    data: { task_ids: [taskId], type },
  })
  if (!res.ok()) {
    throw new Error(`Failed to assign ${type}: ${res.status()} ${await res.text()}`)
  }
}

async function assignReward(
  request: APIRequestContext,
  childId: string,
  rewardId: string,
): Promise<void> {
  const res = await request.put(`${BACKEND}/child/${childId}/set-rewards`, {
    data: { reward_ids: [rewardId] },
  })
  if (!res.ok()) {
    throw new Error(`Failed to assign reward: ${res.status()} ${await res.text()}`)
  }
}

async function assignRoutine(
  request: APIRequestContext,
  childId: string,
  routineId: string,
): Promise<void> {
  const res = await request.post(`${BACKEND}/child/${childId}/assign-routine`, {
    data: { routine_id: routineId },
  })
  if (!res.ok()) {
    throw new Error(`Failed to assign routine: ${res.status()} ${await res.text()}`)
  }
}

function helpButton(page: Page) {
  return page.getByRole('button', { name: 'Show help for this screen' })
}

function modalBackdrop(page: Page) {
  return page.locator('.modal-backdrop')
}

async function dismissAutoTutorialChain(page: Page): Promise<void> {
  const card = page.locator('.tutorial-root .card')
  for (let i = 0; i < 10; i++) {
    if (!(await card.isVisible().catch(() => false))) return
    await page.locator('.tutorial-root .btn-primary').click()
    await page.waitForTimeout(200)
  }
}

function sectionByHeading(page: Page, heading: string) {
  return page.locator('.child-list-container').filter({
    has: page.locator('h3', { hasText: heading }),
  })
}

test.describe('Dialog help button and titles', () => {
  test.describe.configure({ mode: 'serial' })

  let childId = ''
  let choreId = ''
  let kindnessId = ''
  let penaltyId = ''
  let rewardId = ''
  let routineId = ''

  test.beforeAll(async ({ request }) => {
    await setTutorialEnabled(request, true)
    await resetTutorialProgress(request)

    childId = await createChild(request, CHILD_NAME)
    choreId = await createTask(request, CHORE_NAME, 'chore')
    kindnessId = await createTask(request, KINDNESS_NAME, 'kindness')
    penaltyId = await createTask(request, PENALTY_NAME, 'penalty')
    rewardId = await createReward(request, REWARD_NAME)
    routineId = await createRoutine(request, ROUTINE_NAME)

    await assignTask(request, childId, choreId, 'chore')
    await assignTask(request, childId, kindnessId, 'kindness')
    await assignTask(request, childId, penaltyId, 'penalty')
    await assignReward(request, childId, rewardId)
    await assignRoutine(request, childId, routineId)

    // Give the child enough points so the reward is ready to redeem.
    await request.put(`${BACKEND}/child/${childId}/edit`, { data: { points: REWARD_COST } })
  })

  test.afterAll(async ({ request }) => {
    if (childId) await request.delete(`${BACKEND}/child/${childId}`)
    if (choreId) await request.delete(`${BACKEND}/task/${choreId}`)
    if (kindnessId) await request.delete(`${BACKEND}/task/${kindnessId}`)
    if (penaltyId) await request.delete(`${BACKEND}/task/${penaltyId}`)
    if (rewardId) await request.delete(`${BACKEND}/reward/${rewardId}`)
    if (routineId) await request.delete(`${BACKEND}/routine/${routineId}`)
    await setTutorialEnabled(request, false)
    await resetTutorialProgress(request)
  })

  test.beforeEach(async ({ page }) => {
    await page.goto(`/parent/${childId}`)
    await expect(page.getByText(CHILD_NAME, { exact: true }).first()).toBeVisible({ timeout: 10000 })
    await dismissAutoTutorialChain(page)
    await expect(helpButton(page)).toBeVisible({ timeout: 5000 })
  })

  test('Task confirm dialog hides help button and shows "Confirm Task" for chores', async ({
    page,
  }) => {
    const card = sectionByHeading(page, 'Chores').locator('.item-card').filter({ hasText: CHORE_NAME })
    await card.waitFor({ state: 'visible' })
    await card.click()
    await expect(card).toHaveClass(/item-ready/, { timeout: 3000 })
    await card.click()

    await expect(modalBackdrop(page)).toBeVisible({ timeout: 3000 })
    await expect(page.locator('.modal-title')).toHaveText('Confirm Task')
    await expect(helpButton(page)).not.toBeVisible()

    await page.getByRole('button', { name: 'Cancel' }).click()
    await expect(modalBackdrop(page)).not.toBeVisible({ timeout: 3000 })
    await expect(helpButton(page)).toBeVisible({ timeout: 5000 })
  })

  test('Task confirm dialog hides help button and shows "Confirm Act" for kindness acts', async ({
    page,
  }) => {
    const card = sectionByHeading(page, 'Kindness Acts')
      .locator('.item-card')
      .filter({ hasText: KINDNESS_NAME })
    await card.waitFor({ state: 'visible' })
    await card.click()
    await expect(card).toHaveClass(/item-ready/, { timeout: 3000 })
    await card.click()

    await expect(modalBackdrop(page)).toBeVisible({ timeout: 3000 })
    await expect(page.locator('.modal-title')).toHaveText('Confirm Act')
    await expect(helpButton(page)).not.toBeVisible()

    await page.getByRole('button', { name: 'Cancel' }).click()
    await expect(modalBackdrop(page)).not.toBeVisible({ timeout: 3000 })
    await expect(helpButton(page)).toBeVisible({ timeout: 5000 })
  })

  test('Task confirm dialog hides help button for penalties', async ({ page }) => {
    const card = sectionByHeading(page, 'Penalties')
      .locator('.item-card')
      .filter({ hasText: PENALTY_NAME })
    await card.waitFor({ state: 'visible' })
    await card.click()
    await expect(card).toHaveClass(/item-ready/, { timeout: 3000 })
    await card.click()

    await expect(modalBackdrop(page)).toBeVisible({ timeout: 3000 })
    await expect(page.locator('.modal-title')).toHaveText('Confirm Penalty')
    await expect(helpButton(page)).not.toBeVisible()

    await page.getByRole('button', { name: 'Cancel' }).click()
    await expect(modalBackdrop(page)).not.toBeVisible({ timeout: 3000 })
    await expect(helpButton(page)).toBeVisible({ timeout: 5000 })
  })

  test('Reward confirm dialog hides help button and shows "Grant Reward"', async ({ page }) => {
    const card = sectionByHeading(page, 'Rewards').locator('.item-card').filter({ hasText: REWARD_NAME })
    await card.waitFor({ state: 'visible' })
    await expect(card.getByText('REWARD READY')).toBeVisible()

    await card.click()
    await expect(card).toHaveClass(/item-ready/, { timeout: 3000 })
    await card.click()

    await expect(modalBackdrop(page)).toBeVisible({ timeout: 3000 })
    await expect(page.locator('.modal-title')).toHaveText('Grant Reward')
    await expect(helpButton(page)).not.toBeVisible()

    await page.getByRole('button', { name: 'No', exact: true }).click()
    await expect(modalBackdrop(page)).not.toBeVisible({ timeout: 3000 })
    await expect(helpButton(page)).toBeVisible({ timeout: 5000 })
  })

  test('Routine confirm dialog hides help button and shows "Confirm Routine"', async ({ page }) => {
    const card = sectionByHeading(page, 'Routines').locator('.item-card').filter({ hasText: ROUTINE_NAME })
    await card.waitFor({ state: 'visible' })
    await card.click()
    await expect(card).toHaveClass(/item-ready/, { timeout: 3000 })
    await card.click()

    await expect(modalBackdrop(page)).toBeVisible({ timeout: 3000 })
    await expect(page.locator('.modal-title')).toHaveText('Confirm Routine')
    await expect(helpButton(page)).not.toBeVisible()

    await page.getByRole('button', { name: 'Cancel' }).click()
    await expect(modalBackdrop(page)).not.toBeVisible({ timeout: 3000 })
    await expect(helpButton(page)).toBeVisible({ timeout: 5000 })
  })

  test('help button replays the full tutorial chain when tutorial tips are disabled', async ({
    page,
    request,
  }) => {
    await setTutorialEnabled(request, false)
    await resetTutorialProgress(request)

    await page.goto(`/parent/${childId}`)
    await expect(page.getByText(CHILD_NAME, { exact: true }).first()).toBeVisible({ timeout: 10000 })
    await expect(page.locator('.tutorial-root .card')).not.toBeVisible()
    await expect(helpButton(page)).toBeVisible({ timeout: 5000 })

    await helpButton(page).click()

    const cardTitle = page.locator('.tutorial-root .card .title')
    await expect(cardTitle).toHaveText("This is your child's page")

    await page.locator('.tutorial-root .btn-primary').click()
    await expect(cardTitle).toHaveText('Assign chores')

    await page.locator('.tutorial-root .btn-primary').click()
    await expect(cardTitle).toHaveText('Assign kindness acts')

    await page.locator('.tutorial-root .btn-primary').click()
    await expect(cardTitle).toHaveText('Assign rewards')

    await page.locator('.tutorial-root .btn-primary').click()
    await expect(cardTitle).toHaveText('Assign routines')

    await page.locator('.tutorial-root .btn-primary').click()
    await expect(cardTitle).toHaveText('Assign penalties')

    await page.locator('.tutorial-root .btn-primary').click()
    await expect(cardTitle).toHaveText('Quick actions')

    await page.locator('.tutorial-root .btn-primary').click()
    await expect(page.locator('.tutorial-root .card')).not.toBeVisible()
  })
})
