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

export const PROPOSAL_ID = '11111111-1111-4111-8111-111111111111'
export const PROJECT_ID = '22222222-2222-4222-8222-222222222222'
export const DOC_PATH = 'nexus/mission-control.md'
export const MEMORY_NAME = '2026-07-31.md'
export const INCIDENT_NAME = '2026-07-30-gateway-timeout.md'

type NexusRole = 'owner' | 'member'
type ProposalStatus =
  | 'awaiting_approval'
  | 'provisioning'
  | 'ready'
  | 'partial'
  | 'failed'
  | 'in_doubt'
  | 'rejected'

interface MockOptions {
  authenticated?: boolean
  role?: NexusRole
  proposalStatus?: ProposalStatus
  domainResyncSequence?: number
  boardRefreshDelayMs?: number
  boardRefreshTitle?: string
  apiFailure?: {
    path: string
    status: number
    attempts?: number
  }
}

interface CapturedRequest {
  path: string
  method: string
  headers: Record<string, string>
  body: unknown
}

export interface NexusApiHarness {
  readonly proposalRequests: CapturedRequest[]
  readonly boardRequestUrls: string[]
  readonly domainEventRequests: Array<{ lastEventId: string | null }>
  readonly taskMutationRequests: CapturedRequest[]
  readonly chatRequests: CapturedRequest[]
  readonly resyncResponseCount: number
  readonly openClawOverviewRequestCount: number
  readonly browserTelemetryRequestCount: number
  readonly proposal: Record<string, unknown>
}

const now = '2026-07-31T10:00:00.000Z'

function collection(items: unknown[] = []) {
  return {
    state: 'ready',
    items,
    nextCursor: null,
    message: null,
    recovery: null,
    checkedAt: now,
  }
}

function task(
  id: string,
  title: string,
  state: string,
  overrides: Record<string, unknown> = {},
) {
  return {
    id,
    title,
    detail: `${title} detail`,
    source: 'bao',
    state,
    priority: 'Medium',
    assignedTo: 'bao',
    parentTaskId: null,
    projectId: null,
    dueDate: null,
    createdAt: now,
    updatedAt: now,
    isAgentTask: false,
    expectedFrom: null,
    lastActivityMessage: 'Visible E2E activity',
    lastActivityAt: now,
    childTaskCount: 0,
    openChildTaskCount: 0,
    hasVisibleDelegation: false,
    childTasks: [],
    ...overrides,
  }
}

const openTask = task('task-open-1', 'E2E Backlog task', 'Backlog')
const progressTask = task('task-progress-1', 'E2E Active task', 'In progress')
const firstDoneTask = task('task-done-1', 'E2E Done page one', 'Done')
const secondDoneTask = task('task-done-2', 'E2E Done page two', 'Done')
const projectTask = task('task-project-1', 'E2E Project task', 'In progress', {
  projectId: PROJECT_ID,
  assignedTo: 'iris',
  expectedFrom: 'iris',
  isAgentTask: true,
})
const unrelatedTask = task('task-unrelated-1', 'E2E Unrelated task', 'Backlog', {
  projectId: null,
})

const projectFixture = {
  id: PROJECT_ID,
  name: 'Release Readiness',
  description: 'Coordinates the release across tasks, agents, and runs.',
  status: 'Online',
  progress: 64,
  createdAt: now,
  updatedAt: now,
}

const projectRunFixture = {
  id: '33333333-3333-4333-8333-333333333333',
  title: 'Release readiness verification',
  prompt: 'Verify the linked project.',
  agentId: 'iris',
  sessionKey: 'agent:iris:project-release',
  status: 'completed',
  taskId: projectTask.id,
  projectId: PROJECT_ID,
  openClawRunId: 'openclaw-project-run-1',
  retriedFromRunId: null,
  correlationId: 'e2e-project-correlation',
  actor: 'bao',
  lastError: null,
  lastGatewaySequence: 42,
  sequenceGapDetected: false,
  canStop: false,
  canRetry: true,
  canResume: false,
  resumeCapabilityMessage: 'Completed runs cannot be resumed.',
  createdAt: now,
  updatedAt: now,
  startedAt: now,
  finishedAt: now,
}

const agentDetailFixture = {
  id: 'iris',
  name: 'Iris',
  role: 'Chief of Staff',
  model: 'openai/gpt-5.4',
  status: 'Online',
  lastSeen: now,
  workspace: '/managed/agents/iris',
  agentDir: '/managed/agents/iris/agent',
  description: 'Coordinates E2E work.',
  subAgents: ['release-sentinel'],
  identityName: 'Iris',
}

const agentActivityFixture = [
  {
    id: 81,
    type: 'task',
    message: 'Verified the OpenClaw content inventory.',
    at: now,
    source: 'activity',
    relativeTime: 'just now',
  },
]

const agentSummaryFixture = {
  now: {
    text: 'Verified the OpenClaw content inventory.',
    source: 'nexus-activity',
    timestamp: now,
  },
  today: {
    text: 'Last 24h: Verified the OpenClaw content inventory.',
    source: 'nexus-activity',
    timestamp: now,
  },
  generatedAt: now,
}

const standardAgentFiles = [
  'AGENTS.md',
  'SOUL.md',
  'TOOLS.md',
  'IDENTITY.md',
  'USER.md',
  'HEARTBEAT.md',
  'BOOTSTRAP.md',
  'MEMORY.md',
] as const

const agentFileCollectionFixture = {
  agentId: 'iris',
  files: standardAgentFiles.map((name, index) => ({
    name,
    missing: false,
    size: 64 + index,
    updatedAt: now,
    contentHash: `sha256-e2e-${name.toLowerCase().replace('.', '-')}`,
  })),
  checkedAt: now,
}

const docFixture = {
  name: 'mission-control.md',
  path: DOC_PATH,
  category: 'nexus',
  type: 'md',
  size: 112,
  modifiedAt: now,
  sourceAgentId: 'iris',
  workspacePath: DOC_PATH,
}

const docDetailFixture = {
  name: docFixture.name,
  path: docFixture.path,
  content: '# Mission Control Runbook\n\nOpenClaw-backed documentation is visible in Nexus.',
  size: docFixture.size,
  modifiedAt: docFixture.modifiedAt,
  sourceAgentId: docFixture.sourceAgentId,
  workspacePath: docFixture.workspacePath,
}

const memoryFixture = {
  name: MEMORY_NAME,
  path: MEMORY_NAME,
  size: 96,
  modifiedAt: now,
  sourceAgentId: 'iris',
  workspacePath: `memory/${MEMORY_NAME}`,
}

const memoryDetailFixture = {
  ...memoryFixture,
  content: '# Daily Memory\n\nAgent-first context is loaded from Iris.',
}

const memorySearchFixture = {
  name: memoryFixture.name,
  path: memoryFixture.path,
  excerpt: 'Agent-first context is loaded from Iris.',
  size: memoryFixture.size,
  sourceAgentId: memoryFixture.sourceAgentId,
  workspacePath: memoryFixture.workspacePath,
}

const incidentFixture = {
  name: INCIDENT_NAME,
  title: 'Gateway Timeout Recovered',
  date: '2026-07-30',
  severity: 'major',
  excerpt: 'The gateway recovered without duplicate provisioning.',
  size: 144,
  sourceAgentId: 'iris',
  workspacePath: `memory/incidents/${INCIDENT_NAME}`,
}

const incidentDetailFixture = {
  name: incidentFixture.name,
  title: incidentFixture.title,
  date: incidentFixture.date,
  content: [
    '# Gateway Timeout Recovered',
    '',
    '**Severity:** major',
    '',
    'The gateway recovered without duplicate provisioning.',
  ].join('\n'),
  size: incidentFixture.size,
  sourceAgentId: incidentFixture.sourceAgentId,
  workspacePath: incidentFixture.workspacePath,
}

const securityStatusFixture = {
  authMethod: 'JWT + PBKDF2',
  tokenConfig: {
    issuer: 'nexus',
    audience: 'nexus-web',
    refreshTokenDays: 7,
    accessTokenMinutes: 30,
  },
  rateLimit: '5 login attempts per minute per IP',
  passwordPolicy: 'Minimum 10 characters',
  cookieConfig: {
    httpOnly: true,
    secure: true,
    sameSite: 'Strict',
  },
  twoFactorEnabled: false,
  passkeyEnabled: false,
  checkedAt: now,
}

function buildProposal(status: ProposalStatus = 'awaiting_approval'): Record<string, unknown> {
  return {
    id: PROPOSAL_ID,
    source: 'manual',
    requestedName: 'Release Sentinel',
    requestedAgentId: 'release-sentinel',
    role: 'Release Operations',
    description: 'Verifies release readiness and reports blockers.',
    model: 'openai/gpt-5.4',
    emoji: null,
    avatar: null,
    workspace: '/managed/agents/release-sentinel',
    files: [
      {
        name: 'SOUL.md',
        contentHash: 'abc123def456abc123def456',
        size: 42,
        content: '# Release Sentinel\nVerify before reporting.',
      },
    ],
    status,
    requestedBy: 'Bao Owner',
    approvedBy: status === 'ready' ? 'Bao Owner' : null,
    rejectedBy: null,
    rejectionReason: null,
    openClawAgentId: status === 'ready' ? 'release-sentinel' : null,
    openClawWorkspace: status === 'ready' ? '/managed/agents/release-sentinel' : null,
    error: null,
    revision: status === 'ready' ? 2 : 1,
    createdAt: now,
    updatedAt: now,
    approvedAt: status === 'ready' ? now : null,
    rejectedAt: null,
    completedAt: status === 'ready' ? now : null,
  }
}

function openClawOverview() {
  const agents = [
    {
      id: 'iris',
      name: 'Iris',
      description: 'Coordinates E2E work.',
      model: 'openai/gpt-5.4',
      provider: 'openai',
      workspace: '/managed/agents/iris',
      status: 'ready',
    },
  ]
  return {
    connection: {
      state: 'connected',
      configured: true,
      credentialConfigured: true,
      connected: true,
      endpoint: 'ws://openclaw-gateway:18789',
      gatewayVersion: '2026.7.1',
      requiredVersion: '2026.7.1',
      versionPinned: true,
      versionMatches: true,
      protocolVersion: 4,
      grantedScopes: ['operator.read', 'operator.admin'],
      advertisedEvents: [],
      lastConnectedAt: now,
      lastEventAt: now,
      reconnectAttempts: 0,
      message: 'Connected',
      recovery: null,
      checkedAt: now,
      deviceId: 'nexus-e2e',
      pairingRequired: false,
      pairingRequestId: null,
    },
    capabilities: [],
    tasks: collection([]),
    sessions: collection([]),
    cronJobs: collection([]),
    approvals: collection([]),
    activity: collection([]),
    models: collection([]),
    agents: collection(agents),
    generatedAt: now,
  }
}

function operationsSnapshot() {
  return {
    generatedAt: now,
    runtime: {
      runtime: 'OpenClaw',
      status: 'Connected',
      detail: 'E2E fixture',
    },
    models: [],
    metrics: {
      activeAgents: 1,
      queuedTasks: 2,
      successRate: 100,
      incidents: 0,
    },
    projects: [],
    tasks: [],
    activity: [],
  }
}

async function fulfillJson(route: Route, body: unknown, status = 200) {
  await route.fulfill({
    status,
    contentType: 'application/json',
    body: JSON.stringify(body),
  })
}

function requestBody(request: Request): unknown {
  const raw = request.postData()
  if (!raw) return null
  try {
    return JSON.parse(raw)
  } catch {
    return raw
  }
}

function problem(detail: string, status: number) {
  return {
    type: 'about:blank',
    title: status === 403 ? 'Forbidden' : 'Not found',
    status,
    detail,
  }
}

function operationResult(
  status: string,
  primaryRef: { type: string; id: string; label?: string | null },
  affectedRefs: Array<{ type: string; id: string; label?: string | null }> = [],
) {
  return {
    operationId: `e2e-${status}-${primaryRef.id}`,
    status,
    revision: 1,
    primaryRef,
    affectedRefs,
    traceId: 'e2e-trace-1',
  }
}

export async function mockNexusApi(
  page: Page,
  options: MockOptions = {},
): Promise<NexusApiHarness> {
  const authenticated = options.authenticated ?? true
  const role = options.role ?? 'owner'
  const proposalRequests: CapturedRequest[] = []
  const boardRequestUrls: string[] = []
  const domainEventRequests: Array<{ lastEventId: string | null }> = []
  const taskMutationRequests: CapturedRequest[] = []
  const chatRequests: CapturedRequest[] = []
  let proposal = buildProposal(options.proposalStatus)
  let resyncResponseCount = 0
  let boardInitialRequestCount = 0
  let openClawOverviewRequestCount = 0
  let browserTelemetryRequestCount = 0
  let injectedFailureCount = 0
  let openTaskState = 'Backlog'

  await page.route('**/api/**', async route => {
    const request = route.request()
    const url = new URL(request.url())
    const { pathname: path } = url
    const method = request.method()

    // The Vite source graph contains paths such as /src/api/client.ts, which
    // also match Playwright's **/api/** glob. Only intercept the actual HTTP
    // API boundary and let application modules continue to the dev server.
    if (!path.startsWith('/api/')) {
      await route.fallback()
      return
    }

    if (
      options.apiFailure
      && path === options.apiFailure.path
      && injectedFailureCount < (options.apiFailure.attempts ?? 1)
    ) {
      injectedFailureCount += 1
      await fulfillJson(
        route,
        problem(
          'The requested Nexus dependency is temporarily unavailable.',
          options.apiFailure.status,
        ),
        options.apiFailure.status,
      )
      return
    }

    if (path === '/api/v1/auth/refresh' && method === 'POST') {
      if (!authenticated) {
        await fulfillJson(route, problem('No active E2E session.', 401), 401)
        return
      }
      await fulfillJson(route, {
        accessToken: `e2e-${role}-token`,
        expiresAt: '2026-08-01T10:00:00.000Z',
        user: {
          id: role === 'owner' ? 'owner-e2e' : 'member-e2e',
          email: role === 'owner' ? 'bao@example.test' : 'viewer@example.test',
          displayName: role === 'owner' ? 'Bao Owner' : 'Nexus Viewer',
          role,
        },
      })
      return
    }

    if (path === '/api/v1/auth/logout' && method === 'POST') {
      await route.fulfill({ status: 204 })
      return
    }

    if (path === '/api/v1/events') {
      const lastEventId = request.headers()['last-event-id'] ?? null
      domainEventRequests.push({ lastEventId })

      if (options.domainResyncSequence && resyncResponseCount === 0) {
        const deadline = Date.now() + 2_000
        while (!boardRequestUrls.some(value => !value.includes('doneCursor=')) && Date.now() < deadline) {
          await new Promise(resolve => setTimeout(resolve, 20))
        }
        // Let the initial query paint before the stream requests a REST resync.
        await new Promise(resolve => setTimeout(resolve, 120))
        resyncResponseCount += 1
        await route.fulfill({
          status: 200,
          contentType: 'text/event-stream',
          headers: { 'Cache-Control': 'no-cache' },
          body: [
            `id: ${options.domainResyncSequence}`,
            'event: resync_required',
            `data: ${JSON.stringify({
              sequence: options.domainResyncSequence,
              eventType: 'resync_required',
              entity: { type: 'event-stream', id: '*', label: null },
              entityRevision: `stream-${options.domainResyncSequence}`,
              occurredAt: now,
              payload: { reason: 'stale_or_missing_cursor' },
            })}`,
            '',
            '',
          ].join('\n'),
        })
        return
      }

      const cursorLine = options.domainResyncSequence
        ? `id: ${options.domainResyncSequence}\n`
        : ''
      await route.fulfill({
        status: 200,
        contentType: 'text/event-stream',
        headers: { 'Cache-Control': 'no-cache' },
        body: `${cursorLine}event: heartbeat\ndata: {}\n\n`,
      })
      return
    }

    if (path === '/api/v1/projects' && method === 'GET') {
      await fulfillJson(route, [projectFixture])
      return
    }
    if (path === '/api/v1/activity' && method === 'GET') {
      await fulfillJson(route, {
        items: [{
          id: 71,
          type: 'task.updated',
          message: 'Nexus task deep link is ready.',
          at: now,
          entity: {
            type: 'task',
            id: openTask.id,
            label: openTask.title,
          },
        }],
        totalCount: 1,
        page: 1,
        pageSize: 200,
        totalPages: 1,
      })
      return
    }
    if (path === `/api/v1/projects/${PROJECT_ID}` && method === 'GET') {
      await fulfillJson(route, projectFixture)
      return
    }
    if (path === `/api/v1/projects/${PROJECT_ID}/tasks` && method === 'GET') {
      await fulfillJson(route, [projectTask])
      return
    }
    if (path === '/api/v1/tasks' && method === 'GET') {
      await fulfillJson(route, [projectTask, unrelatedTask])
      return
    }

    if (path === '/api/v1/operations/snapshot') {
      await fulfillJson(route, operationsSnapshot())
      return
    }
    if (path === '/api/v1/routing') {
      await fulfillJson(route, [])
      return
    }
    if (path === '/api/v1/openclaw/overview') {
      openClawOverviewRequestCount += 1
      await fulfillJson(route, openClawOverview())
      return
    }
    if (path === '/api/v1/openclaw/capabilities') {
      await fulfillJson(route, [])
      return
    }
    if (path === '/api/v1/openclaw/models/auth-status' && method === 'GET') {
      await fulfillJson(route, collection([{
        provider: 'openai',
        displayName: 'OpenAI',
        status: 'ok',
        expiry: null,
        profiles: [{ type: 'oauth', status: 'ok', count: 1 }],
        apiKey: null,
        usage: { summary: 'Ready', plan: 'team' },
      }]))
      return
    }
    if (path === '/api/v1/openclaw/agents' && method === 'GET') {
      await fulfillJson(route, openClawOverview().agents)
      return
    }
    if (path === '/api/v1/agents/iris' && method === 'GET') {
      await fulfillJson(route, agentDetailFixture)
      return
    }
    if (path === '/api/v1/agents/iris/activity' && method === 'GET') {
      await fulfillJson(route, agentActivityFixture)
      return
    }
    if (path === '/api/v1/agents/iris/summary' && method === 'GET') {
      await fulfillJson(route, agentSummaryFixture)
      return
    }
    if (
      path === '/api/v1/openclaw/agents/iris/files'
      && method === 'GET'
    ) {
      if (role !== 'owner') {
        await fulfillJson(route, problem('Owner access is required.', 403), 403)
        return
      }
      await fulfillJson(route, agentFileCollectionFixture)
      return
    }
    const agentFileMatch = path.match(
      /^\/api\/v1\/openclaw\/agents\/iris\/files\/([^/]+)$/,
    )
    if (agentFileMatch && method === 'GET') {
      if (role !== 'owner') {
        await fulfillJson(route, problem('Owner access is required.', 403), 403)
        return
      }
      const fileName = decodeURIComponent(agentFileMatch[1])
      const summary = agentFileCollectionFixture.files.find(
        file => file.name === fileName,
      )
      if (!summary) {
        await fulfillJson(route, problem('Agent file not found.', 404), 404)
        return
      }
      await fulfillJson(route, {
        agentId: 'iris',
        ...summary,
        content: `# ${fileName}\n\nOpenClaw returned the authoritative Iris file.`,
        checkedAt: now,
      })
      return
    }
    if (
      path === '/api/v1/openclaw/agents/iris/workspace'
      && method === 'GET'
    ) {
      if (role !== 'owner') {
        await fulfillJson(route, problem('Owner access is required.', 403), 403)
        return
      }
      await fulfillJson(route, {
        agentId: 'iris',
        path: url.searchParams.get('path') ?? '',
        parentPath: null,
        entries: [],
        totalEntries: 0,
        offset: Number(url.searchParams.get('offset') ?? 0),
        checkedAt: now,
      })
      return
    }
    if (path === '/api/v1/docs' && method === 'GET') {
      if (role !== 'owner') {
        await fulfillJson(route, problem('Owner access is required.', 403), 403)
        return
      }
      await fulfillJson(route, [docFixture])
      return
    }
    if (path.startsWith('/api/v1/docs/') && method === 'GET') {
      if (role !== 'owner') {
        await fulfillJson(route, problem('Owner access is required.', 403), 403)
        return
      }
      const requestedPath = decodeURIComponent(
        path.slice('/api/v1/docs/'.length),
      )
      await fulfillJson(
        route,
        requestedPath === DOC_PATH
          ? docDetailFixture
          : problem('Document not found.', 404),
        requestedPath === DOC_PATH ? 200 : 404,
      )
      return
    }
    if (path === '/api/v1/memory' && method === 'GET') {
      if (role !== 'owner') {
        await fulfillJson(route, problem('Owner access is required.', 403), 403)
        return
      }
      await fulfillJson(route, [memoryFixture])
      return
    }
    if (path === '/api/v1/memory/search' && method === 'GET') {
      if (role !== 'owner') {
        await fulfillJson(route, problem('Owner access is required.', 403), 403)
        return
      }
      const query = url.searchParams.get('q')?.toLocaleLowerCase() ?? ''
      await fulfillJson(
        route,
        query.includes('agent') ? [memorySearchFixture] : [],
      )
      return
    }
    const memoryMatch = path.match(/^\/api\/v1\/memory\/([^/]+)$/)
    if (memoryMatch && method === 'GET') {
      if (role !== 'owner') {
        await fulfillJson(route, problem('Owner access is required.', 403), 403)
        return
      }
      const requestedName = decodeURIComponent(memoryMatch[1])
      await fulfillJson(
        route,
        requestedName === MEMORY_NAME
          ? memoryDetailFixture
          : problem('Memory file not found.', 404),
        requestedName === MEMORY_NAME ? 200 : 404,
      )
      return
    }
    if (path === '/api/v1/incidents' && method === 'GET') {
      if (role !== 'owner') {
        await fulfillJson(route, problem('Owner access is required.', 403), 403)
        return
      }
      await fulfillJson(route, [incidentFixture])
      return
    }
    const incidentMatch = path.match(/^\/api\/v1\/incidents\/([^/]+)$/)
    if (incidentMatch && method === 'GET') {
      if (role !== 'owner') {
        await fulfillJson(route, problem('Owner access is required.', 403), 403)
        return
      }
      const requestedName = decodeURIComponent(incidentMatch[1])
      await fulfillJson(
        route,
        requestedName === INCIDENT_NAME
          ? incidentDetailFixture
          : problem('Incident not found.', 404),
        requestedName === INCIDENT_NAME ? 200 : 404,
      )
      return
    }
    if (path === '/api/v1/security/status' && method === 'GET') {
      await fulfillJson(route, securityStatusFixture)
      return
    }
    if (path === '/api/v1/openclaw/runs' && method === 'GET') {
      await fulfillJson(route, {
        items: url.searchParams.get('projectId') === PROJECT_ID
          ? [projectRunFixture]
          : [],
        nextCursor: null,
        checkedAt: now,
      })
      return
    }
    if (path === '/api/dashboard/chat/messages' && method === 'GET') {
      await fulfillJson(route, [])
      return
    }
    if (path === '/api/v1/chat' && method === 'POST') {
      chatRequests.push({
        path,
        method,
        headers: request.headers(),
        body: requestBody(request),
      })
      await fulfillJson(route, {
        runtime: 'OpenClaw Protocol v4',
        agentId: 'iris',
        conversationId: 'e2e-iris-conversation',
        content: 'OpenClaw accepted the Iris request.',
        runId: 'run-chat-e2e-1',
        state: 'running',
        operation: {
          operationId: 'run-chat-e2e-1',
          status: 'running',
          revision: 1,
          primaryRef: {
            type: 'run',
            id: 'run-chat-e2e-1',
            label: 'Iris agent proposal run',
          },
          affectedRefs: [],
          traceId: null,
        },
      })
      return
    }
    if (path === '/api/dashboard/notifications/unread-count') {
      await fulfillJson(route, { count: 0 })
      return
    }
    if (path === '/api/dashboard/notifications/snapshot') {
      await fulfillJson(route, {
        notifications: [{
          id: '33333333-3333-4333-8333-333333333333',
          type: 'task_assigned',
          title: 'Inspect E2E backlog task',
          message: 'A linked task requires attention.',
          forUser: 'bao',
          taskId: openTask.id,
          isRead: false,
          createdAt: now,
        }],
        unreadCount: 1,
        forUser: 'bao',
      })
      return
    }
    if (
      /^\/api\/dashboard\/notifications\/[^/]+\/read$/.test(path)
      && method === 'PATCH'
    ) {
      const notificationId = path.split('/').at(-2)!
      await fulfillJson(route, {
        id: notificationId,
        type: 'task_assigned',
        title: 'Inspect E2E backlog task',
        message: 'A linked task requires attention.',
        forUser: 'bao',
        taskId: openTask.id,
        isRead: true,
        createdAt: now,
        operation: operationResult(
          'completed',
          { type: 'notification', id: notificationId, label: 'Inspect E2E backlog task' },
          [{ type: 'task', id: openTask.id, label: openTask.title }],
        ),
      })
      return
    }
    if (path === '/api/dashboard/notifications/read-all' && method === 'PATCH') {
      await fulfillJson(route, {
        marked: 1,
        operation: operationResult('completed', {
          type: 'notification',
          id: '*',
          label: 'Benachrichtigungen',
        }),
      })
      return
    }
    if (path === '/api/v1/telemetry/browser') {
      browserTelemetryRequestCount += 1
      await route.fulfill({ status: 202 })
      return
    }

    if (path === '/api/dashboard/agents') {
      await fulfillJson(route, [
        {
          id: 'iris',
          name: 'Iris',
          role: 'Chief of Staff',
          description: 'Coordinates E2E work.',
          tags: ['Orchestration'],
          model: 'openai/gpt-5.4',
          statusLabel: 'Bereit',
          statusKind: 'ready',
          statusDetail: 'Mock runtime is ready.',
          isActive: false,
          progress: null,
          currentTask: null,
        },
      ])
      return
    }

    const isProposalRequest = path === '/api/v1/openclaw/agents/create-options'
      || path.startsWith('/api/v1/openclaw/agent-proposals')
    if (isProposalRequest && role !== 'owner') {
      await fulfillJson(route, problem('Owner access is required.', 403), 403)
      return
    }

    if (path === '/api/v1/openclaw/agents/create-options' && method === 'GET') {
      await fulfillJson(route, {
        canSubmitProposal: true,
        canProvision: true,
        state: 'ready',
        reason: null,
        workspaceRoot: '/managed/agents',
        existingAgentIds: ['iris'],
        models: [
          {
            id: 'openai/gpt-5.4',
            name: 'GPT-5.4',
            provider: 'OpenAI',
            available: true,
          },
          {
            id: 'unavailable/model',
            name: 'Unavailable',
            provider: 'Fixture',
            available: false,
          },
        ],
        standardFiles: ['AGENTS.md', 'SOUL.md', 'TOOLS.md'],
        checkedAt: now,
      })
      return
    }

    if (path === '/api/v1/openclaw/agent-proposals' && method === 'GET') {
      await fulfillJson(route, {
        items: [proposal],
        nextCursor: null,
        checkedAt: now,
      })
      return
    }

    if (path === '/api/v1/openclaw/agent-proposals' && method === 'POST') {
      const body = requestBody(request) as Record<string, unknown>
      proposalRequests.push({
        path,
        method,
        headers: request.headers(),
        body,
      })
      const fileEntries = body.files && typeof body.files === 'object'
        ? Object.entries(body.files as Record<string, string>)
        : []
      const requestedName = String(body.name || 'Unnamed Agent')
      proposal = {
        ...buildProposal('awaiting_approval'),
        requestedName,
        requestedAgentId: requestedName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, ''),
        role: body.role || null,
        description: body.description || null,
        model: body.model || null,
        files: fileEntries.map(([name, content], index) => ({
          name,
          contentHash: `e2e-hash-${index}`,
          size: content.length,
          content,
        })),
      }
      await fulfillJson(route, {
        ok: true,
        state: 'awaiting_approval',
        message: 'Agent-Vorschlag wurde angelegt.',
        proposal,
        recovery: null,
        correlationId: request.headers()['x-correlation-id'] ?? null,
        completedAt: now,
      }, 201)
      return
    }

    if (path === `/api/v1/openclaw/agent-proposals/${PROPOSAL_ID}` && method === 'GET') {
      await fulfillJson(route, proposal)
      return
    }

    const actionMatch = path.match(
      new RegExp(`^/api/v1/openclaw/agent-proposals/${PROPOSAL_ID}/(approve|reject|retry)$`),
    )
    if (actionMatch && method === 'POST') {
      const action = actionMatch[1]
      const body = requestBody(request)
      proposalRequests.push({
        path,
        method,
        headers: request.headers(),
        body,
      })

      if (action === 'approve' || action === 'retry') {
        proposal = {
          ...proposal,
          status: 'ready',
          approvedBy: 'Bao Owner',
          approvedAt: now,
          openClawAgentId: proposal.requestedAgentId,
          openClawWorkspace: proposal.workspace,
          completedAt: now,
          updatedAt: now,
          revision: Number(proposal.revision) + 1,
        }
      } else {
        const actionBody = body as Record<string, unknown>
        proposal = {
          ...proposal,
          status: 'rejected',
          rejectedBy: 'Bao Owner',
          rejectionReason: actionBody.reason || 'Rejected in E2E',
          rejectedAt: now,
          completedAt: now,
          updatedAt: now,
          revision: Number(proposal.revision) + 1,
        }
      }

      await fulfillJson(route, {
        ok: true,
        state: proposal.status,
        message: action === 'reject'
          ? 'Agent-Vorschlag wurde abgelehnt.'
          : 'Agent ist bereit.',
        proposal,
        recovery: null,
        correlationId: request.headers()['x-correlation-id'] ?? null,
        completedAt: now,
      }, action === 'reject' ? 200 : 202)
      return
    }

    if (path === '/api/v1/tasks/board' && method === 'GET') {
      boardRequestUrls.push(url.toString())
      const cursor = url.searchParams.get('doneCursor')
      if (cursor) {
        await fulfillJson(route, {
          revision: 'board-r1',
          offen: [],
          inProgress: [],
          review: [],
          blocked: [],
          done: [secondDoneTask],
          nextDoneCursor: null,
          hasMoreDone: false,
        })
      } else {
        boardInitialRequestCount += 1
        if (boardInitialRequestCount > 1 && options.boardRefreshDelayMs) {
          await new Promise(resolve => setTimeout(resolve, options.boardRefreshDelayMs))
        }
        const currentOpenTask = {
          ...openTask,
          title: boardInitialRequestCount > 1 && options.boardRefreshTitle
            ? options.boardRefreshTitle
            : openTask.title,
          state: openTaskState,
        }
        const columns = {
          offen: openTaskState === 'Backlog' ? [currentOpenTask] : [],
          inProgress: openTaskState === 'In progress'
            ? [currentOpenTask, progressTask]
            : [progressTask],
          review: openTaskState === 'Review' ? [currentOpenTask] : [],
          blocked: openTaskState === 'Blocked' ? [currentOpenTask] : [],
          done: openTaskState === 'Done'
            ? [currentOpenTask, firstDoneTask]
            : [firstDoneTask],
        }
        await fulfillJson(route, {
          revision: boardInitialRequestCount > 1 && options.boardRefreshTitle
            ? 'board-r2'
            : 'board-r1',
          ...columns,
          nextDoneCursor: 'done-page-2',
          hasMoreDone: true,
        })
      }
      return
    }

    if (path === `/api/dashboard/tasks/${openTask.id}` && method === 'GET') {
      await fulfillJson(route, {
        ...openTask,
        state: openTaskState,
      })
      return
    }
    if (path === `/api/dashboard/tasks/${openTask.id}/children` && method === 'GET') {
      await fulfillJson(route, [])
      return
    }
    if (path === `/api/dashboard/tasks/${openTask.id}/activity` && method === 'GET') {
      await fulfillJson(route, [])
      return
    }

    const boardCardMatch = path.match(/^\/api\/v1\/tasks\/([^/]+)\/board-card$/)
    if (boardCardMatch && method === 'GET') {
      const taskId = decodeURIComponent(boardCardMatch[1])
      if (taskId !== openTask.id) {
        await fulfillJson(route, problem('Task not found.', 404), 404)
        return
      }
      await fulfillJson(route, {
        ...openTask,
        state: openTaskState,
        updatedAt: now,
      })
      return
    }

    const moveTaskMatch = path.match(/^\/api\/dashboard\/tasks\/([^/]+)\/move$/)
    if (moveTaskMatch && method === 'PATCH') {
      const body = requestBody(request) as Record<string, unknown>
      taskMutationRequests.push({
        path,
        method,
        headers: request.headers(),
        body,
      })
      const requestedState = String(body.state || 'Backlog')
      const canonicalStates: Record<string, string> = {
        offen: 'Backlog',
        inProgress: 'In progress',
        review: 'Review',
        blocked: 'Blocked',
        done: 'Done',
      }
      if (decodeURIComponent(moveTaskMatch[1]) === openTask.id) {
        openTaskState = canonicalStates[requestedState] ?? requestedState
      }
      await fulfillJson(route, {
        ...openTask,
        state: openTaskState,
        updatedAt: now,
        operation: operationResult('state_updated', {
          type: 'task',
          id: openTask.id,
          label: openTask.title,
        }),
      })
      return
    }

    if (path === '/api/dashboard/tasks/agent-overview') {
      await fulfillJson(route, {
        waitingForBao: [],
        waitingForIris: [],
        waitingForOthers: [],
        staleTasks: [],
        staleThreshold: '02:00:00',
      })
      return
    }
    if (path === '/api/dashboard/tasks') {
      await fulfillJson(route, [openTask, progressTask, firstDoneTask])
      return
    }
    if (path === '/api/dashboard/tasks/board') {
      await fulfillJson(route, {
        offen: [openTask],
        inProgress: [progressTask],
        review: [],
        blocked: [],
        done: [firstDoneTask],
      })
      return
    }

    if (path === '/api/dashboard/status') {
      await fulfillJson(route, {
        gatewayOk: true,
        irisStatus: 'Ready',
        activeAgents: 1,
        pendingTasks: 2,
      })
      return
    }
    if (path === '/api/dashboard/operations' || path === '/api/dashboard/queue') {
      await fulfillJson(route, [])
      return
    }

    await fulfillJson(route, problem(`No E2E fixture for ${method} ${path}.`, 404), 404)
  })

  return {
    proposalRequests,
    boardRequestUrls,
    domainEventRequests,
    taskMutationRequests,
    chatRequests,
    get resyncResponseCount() {
      return resyncResponseCount
    },
    get openClawOverviewRequestCount() {
      return openClawOverviewRequestCount
    },
    get browserTelemetryRequestCount() {
      return browserTelemetryRequestCount
    },
    get proposal() {
      return proposal
    },
  }
}
