import { expect, test } from '@playwright/test'
import {
  DOC_PATH,
  INCIDENT_NAME,
  MEMORY_NAME,
  mockNexusApi,
  PROJECT_ID,
  PROPOSAL_ID,
} from './support/nexusApi'

const coreRoutes = [
  { path: '/dashboard', heading: 'Live-Orchestrierung' },
  { path: '/agents', heading: 'Agents' },
  { path: '/agents/iris', heading: 'Iris' },
  { path: '/agents/new', heading: 'Agent vorschlagen' },
  { path: `/agents/proposals/${PROPOSAL_ID}`, heading: 'Release Sentinel' },
  { path: '/projects', heading: 'Projects' },
  { path: `/projects/${PROJECT_ID}`, heading: 'Release Readiness' },
  { path: '/tasks', heading: 'Aufgaben' },
  { path: '/tasks/task-open-1', heading: 'E2E Backlog task' },
  { path: '/runs', heading: 'Run Control' },
  { path: '/runs/run-e2e-1', heading: 'Run detail' },
  { path: '/calendar', heading: 'Calendar' },
  { path: '/memory', heading: 'Memory' },
  { path: '/docs', heading: 'Docs' },
  { path: '/incidents', heading: 'Incidents' },
  { path: '/models', heading: 'Models' },
  { path: '/activity', heading: 'Activity' },
  { path: '/notifications', heading: 'Benachrichtigungen' },
  { path: '/security', heading: 'Security Center' },
  { path: '/settings', heading: 'Einstellungen' },
] as const

test.describe('authenticated route and deep-link smoke', () => {
  test('redirects an unauthenticated deep link to login and preserves the target', async ({ page }) => {
    await mockNexusApi(page, { authenticated: false })

    await page.goto(`/agents/proposals/${PROPOSAL_ID}`)

    await expect(page).toHaveURL(/\/login\?redirect=/)
    const loginUrl = new URL(page.url())
    expect(loginUrl.pathname).toBe('/login')
    expect(loginUrl.searchParams.get('redirect')).toBe(`/agents/proposals/${PROPOSAL_ID}`)
    await expect(page.getByRole('button', { name: 'Anmelden' })).toBeVisible()
  })

  test('does not present the rolling login window as a lock while attempts remain', async ({ page }) => {
    await mockNexusApi(page, { authenticated: false })
    await page.route('**/api/v1/auth/login', async route => {
      await route.fulfill({
        status: 401,
        contentType: 'application/problem+json',
        headers: {
          'X-RateLimit-Remaining': '4',
          'X-RateLimit-Reset': String(Math.ceil(Date.now() / 1000) + 60),
        },
        body: JSON.stringify({
          message: 'Invalid email or password.',
          remaining: 4,
          retryAfterSeconds: 60,
        }),
      })
    })

    await page.goto('/login')
    await page.getByLabel('E-Mail').fill('release-smoke@example.invalid')
    await page.getByLabel('Passwort', { exact: true }).fill('not-a-real-password')
    await page.getByRole('button', { name: 'Anmelden' }).click()

    await expect(page.getByRole('alert')).toContainText('4 Versuche verbleibend')
    await expect(page.getByText(/Entsperrt in/)).toHaveCount(0)
    await expect(page.getByRole('button', { name: 'Anmelden' })).toBeEnabled()
    await expect(page.getByRole('button', { name: /Gesperrt/ })).toHaveCount(0)
  })

  test('renders the core owner deep links without a client-side exception', async ({ page }) => {
    test.setTimeout(90_000)
    await mockNexusApi(page, { role: 'owner' })
    const pageErrors: string[] = []
    page.on('pageerror', error => pageErrors.push(error.message))

    for (const route of coreRoutes) {
      await page.goto(route.path)
      await expect(page).toHaveURL(new RegExp(`${route.path.replaceAll('/', '\\/')}$`))
      await expect(page.getByRole('heading', { name: route.heading, exact: true }).first()).toBeVisible()
    }

    expect(pageErrors).toEqual([])
  })

  test('links the Projects index to a directly addressable project result', async ({ page }) => {
    await mockNexusApi(page, { role: 'owner' })

    await page.goto('/projects')
    await expect(page.getByRole('heading', { name: 'Projects', exact: true })).toBeVisible()
    const projectLink = page.getByRole('link', { name: /Release Readiness/ })
    await expect(projectLink).toHaveAttribute('href', `/projects/${PROJECT_ID}`)
    await projectLink.click()

    await expect(page).toHaveURL(`/projects/${PROJECT_ID}`)
    await expect(page.getByRole('heading', { name: 'Release Readiness', exact: true })).toBeVisible()
    await expect(page.getByRole('link', { name: /E2E Project task/ })).toBeVisible()
    await expect(page.getByText('E2E Unrelated task', { exact: true })).toHaveCount(0)
    await expect(page.getByRole('link', { name: /Agent iris/ })).toHaveAttribute(
      'href',
      '/agents/iris',
    )
    await expect(page.getByRole('link', {
      name: /Run Release readiness verification/,
    })).toHaveAttribute(
      'href',
      '/runs/33333333-3333-4333-8333-333333333333',
    )
    await expect(page.getByText(
      /A project reference is not delivered by the current incident contract/,
    )).toBeVisible()

    // The detail route is also a durable deep link, not only a client-side
    // transition from the index.
    await page.goto(`/projects/${PROJECT_ID}`)
    await expect(page.getByRole('heading', { name: 'Release Readiness', exact: true })).toBeVisible()
  })

  test('deduplicates the shared OpenClaw overview across dashboard consumers', async ({ page }) => {
    const api = await mockNexusApi(page, { role: 'owner' })

    await page.goto('/dashboard')
    await expect(page.getByRole('heading', {
      name: 'Live-Orchestrierung',
      exact: true,
    })).toBeVisible()

    await expect.poll(() => api.openClawOverviewRequestCount).toBe(1)
    expect(api.browserTelemetryRequestCount).toBe(0)
  })

  test('distinguishes a dependency outage and recovers without hiding the route', async ({ page }, testInfo) => {
    await mockNexusApi(page, {
      role: 'owner',
      apiFailure: {
        path: '/api/v1/projects',
        status: 503,
        // Vue Query performs two bounded retries for safe reads. The visible
        // recovery action is the next, explicit request.
        attempts: 3,
      },
    })

    await page.goto('/projects')
    const recovery = page.locator('[data-state="offline"][data-problem-kind="offline"]')
    await expect(recovery).toBeVisible()
    await expect(recovery).toContainText('Abhängigkeit nicht erreichbar')
    await page.screenshot({
      path: testInfo.outputPath('dependency-recovery.png'),
      fullPage: true,
    })
    await recovery.getByRole('button', { name: 'Erneut prüfen' }).click()

    await expect(page.getByRole('link', { name: /Release Readiness/ })).toBeVisible()
    await expect(recovery).toHaveCount(0)
  })

  test('keeps Iris and run mutation entry points unavailable to non-owners', async ({ page }) => {
    await mockNexusApi(page, { role: 'member' })

    await page.goto('/dashboard?iris=1')
    await expect(page).toHaveURL('/dashboard')
    await expect(page.getByRole('button', {
      name: 'Iris Chat ist nur für Owner verfügbar',
    })).toBeDisabled()
    await expect(page.locator('#iris-chat-dialog')).toHaveCount(0)

    await page.keyboard.press('Control+k')
    const commandDialog = page.getByRole('dialog', {
      name: 'Mission Control commands',
    })
    await expect(commandDialog).toBeVisible()
    await expect(commandDialog.getByRole('button', { name: /Ask Iris/ })).toHaveCount(0)
    await expect(commandDialog.getByRole('button', { name: /Start OpenClaw run/ })).toHaveCount(0)
    await page.keyboard.press('Escape')

    await page.goto('/tasks?iris=1')
    await expect(page).toHaveURL('/tasks')
    await expect(page.getByRole('button', {
      name: 'Iris chat is available to owners only',
    })).toBeDisabled()
    await expect(page.locator('#iris-chat-dialog')).toHaveCount(0)
  })

  test('resolves Activity and Notification results to the linked task', async ({ page }) => {
    await mockNexusApi(page, { role: 'owner' })

    await page.goto('/activity')
    await page.getByRole('button', { name: /Nexus task deep link is ready/ }).click()
    const activityDialog = page.getByRole('dialog', { name: 'task.updated' })
    const activityTaskLink = activityDialog.getByRole('link', {
      name: /E2E Backlog task/,
    })
    await expect(activityTaskLink).toHaveAttribute('href', '/tasks/task-open-1')
    await activityTaskLink.click()
    await expect(page).toHaveURL('/tasks/task-open-1')

    await page.goto('/notifications')
    await page.getByRole('button', {
      name: /Inspect E2E backlog task/,
    }).click()
    await expect(page).toHaveURL('/tasks/task-open-1')
    await expect(page.getByRole('heading', {
      level: 1,
      name: 'E2E Backlog task',
    })).toBeVisible()
  })

  test('links a Dashboard focus result directly to its task detail', async ({ page }) => {
    await mockNexusApi(page, { role: 'owner' })

    await page.goto('/dashboard')
    const taskLink = page.getByRole('link', {
      name: /E2E Backlog task/,
    })
    await expect(taskLink).toHaveAttribute('href', '/tasks/task-open-1')
    await taskLink.click()

    await expect(page).toHaveURL('/tasks/task-open-1')
    await expect(page.getByRole('heading', {
      level: 1,
      name: 'E2E Backlog task',
    })).toBeVisible()
  })

  test('opens OpenClaw content with its Iris source deep link', async ({ page }) => {
    await mockNexusApi(page, { role: 'owner' })

    await page.goto('/docs')
    await page.getByRole('button', { name: new RegExp(DOC_PATH.split('/').at(-1)!) }).click()
    const docContent = page.locator('.memory-rendered')
    await expect(docContent.getByRole('heading', {
      name: 'Mission Control Runbook',
    })).toBeVisible()
    await expect(docContent).toContainText('OpenClaw-backed documentation is visible in Nexus.')
    await expect(page.locator('.source-context').getByRole('link', {
      name: 'iris',
      exact: true,
    })).toHaveAttribute('href', '/agents/iris')
    await expect(page.locator('.source-context')).toContainText(DOC_PATH)

    await page.goto('/memory')
    await page.getByRole('button', { name: new RegExp(MEMORY_NAME) }).click()
    const memoryContent = page.locator('.memory-rendered')
    await expect(memoryContent.getByRole('heading', {
      name: 'Daily Memory',
    })).toBeVisible()
    await expect(memoryContent).toContainText('Agent-first context is loaded from Iris.')
    await expect(page.locator('.source-context').getByRole('link', {
      name: 'iris',
      exact: true,
    })).toHaveAttribute('href', '/agents/iris')
    await expect(page.locator('.source-context')).toContainText(`memory/${MEMORY_NAME}`)

    await page.goto('/incidents')
    await page.getByRole('button', { name: /Gateway Timeout Recovered/ }).click()
    const incidentContent = page.locator('.incident-rendered')
    await expect(incidentContent.getByRole('heading', {
      name: 'Gateway Timeout Recovered',
    })).toBeVisible()
    await expect(incidentContent).toContainText(
      'The gateway recovered without duplicate provisioning.',
    )
    await expect(page.locator('.source-context').getByRole('link', {
      name: 'iris',
      exact: true,
    })).toHaveAttribute('href', '/agents/iris')
    await expect(page.locator('.source-context')).toContainText(
      `memory/incidents/${INCIDENT_NAME}`,
    )
  })
})

const shellViewports = [
  { width: 375, height: 812 },
  { width: 768, height: 900 },
  { width: 1024, height: 900 },
  { width: 1440, height: 900 },
  { width: 1920, height: 1080 },
]

for (const viewport of shellViewports) {
  test(`keeps every registered core route inside the ${viewport.width}px page shell`, async ({ page }) => {
    test.setTimeout(120_000)
    await page.setViewportSize(viewport)
    await mockNexusApi(page, { role: 'owner' })

    for (const route of coreRoutes) {
      await page.goto(route.path)
      await expect(page.getByRole('heading', { name: route.heading, exact: true }).first()).toBeVisible()
      const geometry = await page.evaluate(() => ({
        viewportWidth: document.documentElement.clientWidth,
        rootOverflow: document.documentElement.scrollWidth - document.documentElement.clientWidth,
        bodyOverflow: document.body.scrollWidth - document.documentElement.clientWidth,
      }))
      expect(
        geometry.rootOverflow,
        `${route.path} overflows the root at ${viewport.width}px`,
      ).toBeLessThanOrEqual(1)
      expect(
        geometry.bodyOverflow,
        `${route.path} overflows the body at ${viewport.width}px`,
      ).toBeLessThanOrEqual(1)
    }
  })
}
