import { QueryClient, type InfiniteData } from '@tanstack/vue-query'
import { describe, expect, it } from 'vitest'
import {
  applyOpenClawCronDetail,
  mergeOpenClawCronPages,
} from '../src/api/openClawCron'
import {
  applyOpenClawRunOperation,
  type OpenClawRunCollectionDto,
  type OpenClawRunDto,
  type OpenClawRunOperationDto,
} from '../src/api/openClawRuns'
import { queryKeys } from '../src/api/queryClient'
import {
  invalidateOpenClawCronEventQueries,
  invalidateOpenClawRunEventQueries,
} from '../src/services/domainEvents'
import type {
  OpenClawCollection,
  OpenClawCronJob,
  OpenClawCronJobDetail,
} from '../src/types/openclaw'

function run(overrides: Partial<OpenClawRunDto> = {}): OpenClawRunDto {
  return {
    id: '98f927bb-3df8-44d0-9ec9-cd98cdcf12db',
    title: 'Review task',
    prompt: 'Review the task.',
    agentId: 'iris',
    sessionKey: 'agent:iris:main',
    status: 'running',
    taskId: '931a9025-f2d6-4e48-8a41-24e8733807db',
    projectId: null,
    openClawRunId: 'oc-run-1',
    retriedFromRunId: null,
    correlationId: 'correlation-1',
    actor: 'bao',
    lastError: null,
    lastGatewaySequence: 1,
    sequenceGapDetected: false,
    canStop: true,
    canRetry: false,
    canResume: false,
    resumeCapabilityMessage: null,
    createdAt: '2026-07-31T10:00:00.000Z',
    updatedAt: '2026-07-31T10:01:00.000Z',
    startedAt: '2026-07-31T10:00:01.000Z',
    finishedAt: null,
    ...overrides,
  }
}

function runCollection(items: OpenClawRunDto[]): OpenClawRunCollectionDto {
  return {
    items,
    nextCursor: 'cursor-1',
    checkedAt: '2026-07-31T10:01:00.000Z',
  }
}

function cronJob(id: string, enabled = true): OpenClawCronJob {
  return {
    id,
    name: `Job ${id}`,
    description: null,
    schedule: '0 7 * * *',
    timeZone: 'Europe/Berlin',
    enabled,
    status: 'idle',
    agentId: 'iris',
    sessionKey: 'agent:iris:main',
    nextRunAt: null,
    lastRunAt: null,
    lastRunStatus: null,
    lastError: null,
    canRun: true,
    resourceHash: `hash-${id}`,
  }
}

function cronDetail(id: string, enabled = true): OpenClawCronJobDetail {
  return {
    id,
    name: `Updated ${id}`,
    displayName: null,
    description: null,
    enabled,
    deleteAfterRun: false,
    agentId: 'iris',
    sessionKey: 'agent:iris:main',
    sessionTarget: 'isolated',
    wakeMode: 'now',
    schedule: {
      kind: 'cron',
      expression: '0 8 * * *',
      timeZone: 'Europe/Berlin',
      at: null,
      everyMs: null,
      anchorMs: null,
      staggerMs: null,
      command: null,
      workingDirectory: null,
    },
    payload: {
      kind: 'agentTurn',
      text: null,
      message: 'Run a bounded check.',
      model: null,
      fallbacks: [],
      thinking: null,
      timeoutSeconds: null,
      allowUnsafeExternalContent: null,
      lightContext: null,
      toolsAllow: [],
      arguments: [],
      workingDirectory: null,
      environmentKeys: [],
      inputConfigured: false,
      noOutputTimeoutSeconds: null,
      outputMaxBytes: null,
    },
    delivery: null,
    trigger: null,
    failureAlert: null,
    createdAt: '2026-07-31T10:00:00.000Z',
    updatedAt: '2026-07-31T10:02:00.000Z',
    nextRunAt: null,
    lastRunAt: null,
    lastRunStatus: null,
    lastError: null,
    resourceHash: `updated-hash-${id}`,
    canUpdate: true,
    canDelete: true,
    canRun: true,
  }
}

describe('OpenClaw Vue Query cache adapters', () => {
  it('patches generated run contracts across list and task-related caches', () => {
    const client = new QueryClient()
    const current = run()
    const other = run({
      id: '568f10e3-4fb0-4af4-acff-53030387ce72',
      taskId: null,
      title: 'Other run',
    })
    const listKey = queryKeys.openClawRuns({ limit: 50 })
    const taskKey = queryKeys.taskRuns(current.taskId!)
    const runningKey = queryKeys.openClawRuns({ limit: 50, status: 'running' })
    client.setQueryData(listKey, runCollection([current, other]))
    client.setQueryData(taskKey, runCollection([current]))
    client.setQueryData(runningKey, runCollection([current]))
    const completed = run({
      status: 'completed',
      canStop: false,
      updatedAt: '2026-07-31T10:03:00.000Z',
      finishedAt: '2026-07-31T10:03:00.000Z',
    })
    const operation: OpenClawRunOperationDto = {
      ok: true,
      state: 'completed',
      message: 'Completed.',
      run: completed,
      resultRun: null,
      completedAt: '2026-07-31T10:03:00.000Z',
    }

    applyOpenClawRunOperation(client, operation)

    expect(client.getQueryData<OpenClawRunCollectionDto>(listKey)?.items)
      .toEqual([completed, other])
    expect(client.getQueryData<OpenClawRunCollectionDto>(listKey)?.nextCursor)
      .toBe('cursor-1')
    expect(client.getQueryData<OpenClawRunCollectionDto>(taskKey)?.items)
      .toEqual([completed])
    expect(client.getQueryData<OpenClawRunCollectionDto>(runningKey)?.items)
      .toEqual([])
    expect(client.getQueryData(queryKeys.openClawRun(completed.id))).toEqual(completed)
  })

  it('merges cron pages and updates only compatible cached lists', () => {
    const client = new QueryClient()
    const enabledKey = queryKeys.openClawCron({ includeDisabled: false, limit: 50 })
    const allKey = queryKeys.openClawCron({ includeDisabled: true, limit: 50 })
    const pages: InfiniteData<OpenClawCollection<OpenClawCronJob>, string | null> = {
      pages: [
        {
          state: 'ready',
          items: [cronJob('a'), cronJob('b')],
          nextCursor: 'next',
          message: null,
          recovery: null,
          checkedAt: '2026-07-31T10:00:00.000Z',
        },
        {
          state: 'ready',
          items: [cronJob('b'), cronJob('c')],
          nextCursor: null,
          message: null,
          recovery: null,
          checkedAt: '2026-07-31T10:01:00.000Z',
        },
      ],
      pageParams: [null, 'next'],
    }
    client.setQueryData(enabledKey, pages)
    client.setQueryData(allKey, pages)

    applyOpenClawCronDetail(client, cronDetail('a', false))

    const enabled = mergeOpenClawCronPages(client.getQueryData(enabledKey))
    const all = mergeOpenClawCronPages(client.getQueryData(allKey))
    expect(enabled?.items.map(item => item.id)).toEqual(['b', 'c'])
    expect(all?.items.find(item => item.id === 'a')).toMatchObject({
      name: 'Updated a',
      enabled: false,
      resourceHash: 'updated-hash-a',
    })
    expect(all?.items.map(item => item.id)).toEqual(['a', 'b', 'c'])
    expect(all?.nextCursor).toBeNull()
  })

  it('invalidates only the affected run and cron domains', async () => {
    const runId = '98f927bb-3df8-44d0-9ec9-cd98cdcf12db'
    const otherRunId = '568f10e3-4fb0-4af4-acff-53030387ce72'
    const cronId = 'cron-a'
    const client = (await import('../src/api/queryClient')).queryClient
    client.clear()
    client.setQueryData(queryKeys.openClawRuns({ limit: 50 }), runCollection([]))
    client.setQueryData(queryKeys.openClawRunHistory(runId), { marker: 'target' })
    client.setQueryData(queryKeys.openClawRunHistory(otherRunId), { marker: 'other' })
    client.setQueryData(queryKeys.openClawCron({ includeDisabled: true, limit: 50 }), { marker: 'list' })
    client.setQueryData(queryKeys.openClawCronDetail(cronId), cronDetail(cronId))
    client.setQueryData(queryKeys.openClawCronDetail('cron-b'), cronDetail('cron-b'))
    client.setQueryData(queryKeys.openClawAgents(), { marker: 'agents' })

    await invalidateOpenClawRunEventQueries(runId)

    expect(client.getQueryState(queryKeys.openClawRuns({ limit: 50 }))?.isInvalidated).toBe(true)
    expect(client.getQueryState(queryKeys.openClawRunHistory(runId))?.isInvalidated).toBe(true)
    expect(client.getQueryState(queryKeys.openClawRunHistory(otherRunId))?.isInvalidated).toBe(false)
    expect(client.getQueryState(queryKeys.openClawAgents())?.isInvalidated).toBe(false)

    await invalidateOpenClawCronEventQueries(cronId)

    expect(client.getQueryState(queryKeys.openClawCron({ includeDisabled: true, limit: 50 }))?.isInvalidated).toBe(true)
    expect(client.getQueryState(queryKeys.openClawCronDetail(cronId))?.isInvalidated).toBe(true)
    expect(client.getQueryState(queryKeys.openClawCronDetail('cron-b'))?.isInvalidated).toBe(false)
    client.clear()
  })
})
