import { computed, toValue, type MaybeRefOrGetter } from 'vue'
import { useQuery, type QueryClient } from '@tanstack/vue-query'
import type { components } from './generated/schema'
import { apiClient } from './client'
import { throwApiProblem } from './contracts'
import { queryKeys } from './queryClient'
import { reportOperationResult } from '../services/operationResults'

export type AgentDetailDto =
  components['schemas']['AgentDetailResponse']
export type AgentActivityDto =
  components['schemas']['AgentActivityResponse']
export type AgentSummaryDto =
  components['schemas']['AgentSummaryResponse']

export type AgentFileCollectionDto =
  components['schemas']['OpenClawAgentFileCollectionDto']
export type AgentFileDto =
  components['schemas']['OpenClawAgentFileDto']
export type AgentFileWriteDto =
  components['schemas']['OpenClawAgentFileWriteDto']

async function requireData<T>(
  response: Response,
  data: T | undefined,
  error: unknown,
  fallback: string,
): Promise<T> {
  if (!response.ok || error || !data) {
    await throwApiProblem(response, fallback)
  }
  return data as T
}

export async function fetchAgentDetail(
  id: string,
  signal?: AbortSignal,
): Promise<AgentDetailDto> {
  const { data, error, response } = await apiClient.GET('/api/v1/agents/{id}', {
    params: { path: { id } },
    signal,
  })
  return requireData(
    response,
    data,
    error,
    `Agent "${id}" wurde nicht gefunden`,
  )
}

export async function fetchAgentActivity(
  id: string,
  signal?: AbortSignal,
): Promise<AgentActivityDto[]> {
  const { data, error, response } = await apiClient.GET(
    '/api/v1/agents/{id}/activity',
    {
      params: { path: { id } },
      signal,
    },
  )
  return requireData(
    response,
    data,
    error,
    'Agent-Aktivität konnte nicht geladen werden',
  )
}

export async function fetchAgentSummary(
  id: string,
  signal?: AbortSignal,
): Promise<AgentSummaryDto> {
  const { data, error, response } = await apiClient.GET(
    '/api/v1/agents/{id}/summary',
    {
      params: { path: { id } },
      signal,
    },
  )
  return requireData(
    response,
    data,
    error,
    'Agent-Zusammenfassung konnte nicht geladen werden',
  )
}

export async function fetchAgentFiles(
  id: string,
  signal?: AbortSignal,
): Promise<AgentFileCollectionDto> {
  const { data, error, response } = await apiClient.GET(
    '/api/v1/openclaw/agents/{agentId}/files',
    {
      params: { path: { agentId: id } },
      signal,
    },
  )
  if (!response.ok || error || !data) {
    await throwApiProblem(response, 'Agent-Dateiliste konnte nicht geladen werden')
  }
  return data as AgentFileCollectionDto
}

export async function fetchAgentFile(
  id: string,
  fileName: string,
  signal?: AbortSignal,
): Promise<AgentFileDto> {
  const { data, error, response } = await apiClient.GET(
    '/api/v1/openclaw/agents/{agentId}/files/{fileName}',
    {
      params: { path: { agentId: id, fileName } },
      signal,
    },
  )
  if (!response.ok || error || !data) {
    await throwApiProblem(response, `${fileName} konnte nicht geladen werden`)
  }
  return data as AgentFileDto
}

export async function applyAgentFileWrite(
  client: QueryClient,
  agentId: string,
  result: AgentFileWriteDto,
) {
  reportOperationResult(result.operation, `${result.file.name} gespeichert`)
  const fileName = result.file.name
  client.setQueryData<AgentFileDto>(
    queryKeys.agentFile(agentId, fileName),
    result.file,
  )
  client.setQueryData<AgentFileCollectionDto>(
    queryKeys.agentFiles(agentId),
    current => current
      ? {
          ...current,
          checkedAt: result.file.checkedAt,
          files: current.files.map(file => file.name === fileName
            ? {
                name: result.file.name,
                missing: result.file.missing,
                size: result.file.size,
                updatedAt: result.file.updatedAt,
                contentHash: result.file.contentHash,
              }
            : file),
        }
      : current,
  )
  await Promise.all([
    client.invalidateQueries({
      queryKey: queryKeys.agentFiles(agentId),
      exact: true,
    }),
    client.invalidateQueries({
      queryKey: queryKeys.agentFile(agentId, fileName),
      exact: true,
      refetchType: 'none',
    }),
  ])
}

function resolvedInput(
  id: MaybeRefOrGetter<string>,
  enabled: MaybeRefOrGetter<boolean>,
) {
  const resolvedId = computed(() => toValue(id))
  const queryEnabled = computed(() => Boolean(resolvedId.value) && toValue(enabled))
  return { resolvedId, queryEnabled }
}

export function useAgentDetailQuery(
  id: MaybeRefOrGetter<string>,
  enabled: MaybeRefOrGetter<boolean> = true,
) {
  const { resolvedId, queryEnabled } = resolvedInput(id, enabled)
  return useQuery({
    queryKey: computed(() => queryKeys.agentDetail(resolvedId.value)),
    queryFn: ({ signal }) => fetchAgentDetail(resolvedId.value, signal),
    enabled: queryEnabled,
  })
}

export function useAgentActivityQuery(
  id: MaybeRefOrGetter<string>,
  enabled: MaybeRefOrGetter<boolean> = true,
) {
  const { resolvedId, queryEnabled } = resolvedInput(id, enabled)
  return useQuery({
    queryKey: computed(() => queryKeys.agentActivity(resolvedId.value)),
    queryFn: ({ signal }) => fetchAgentActivity(resolvedId.value, signal),
    enabled: queryEnabled,
  })
}

export function useAgentSummaryQuery(
  id: MaybeRefOrGetter<string>,
  enabled: MaybeRefOrGetter<boolean> = true,
) {
  const { resolvedId, queryEnabled } = resolvedInput(id, enabled)
  return useQuery({
    queryKey: computed(() => queryKeys.agentSummary(resolvedId.value)),
    queryFn: ({ signal }) => fetchAgentSummary(resolvedId.value, signal),
    enabled: queryEnabled,
  })
}

export function useAgentFilesQuery(
  id: MaybeRefOrGetter<string>,
  enabled: MaybeRefOrGetter<boolean> = true,
) {
  const { resolvedId, queryEnabled } = resolvedInput(id, enabled)
  return useQuery({
    queryKey: computed(() => queryKeys.agentFiles(resolvedId.value)),
    queryFn: ({ signal }) => fetchAgentFiles(resolvedId.value, signal),
    enabled: queryEnabled,
  })
}

export function useAgentFileQuery(
  id: MaybeRefOrGetter<string>,
  fileName: MaybeRefOrGetter<string>,
  enabled: MaybeRefOrGetter<boolean> = true,
) {
  const resolvedId = computed(() => toValue(id))
  const resolvedFileName = computed(() => toValue(fileName))
  const queryEnabled = computed(() =>
    Boolean(resolvedId.value)
    && Boolean(resolvedFileName.value)
    && toValue(enabled),
  )
  return useQuery({
    queryKey: computed(() =>
      queryKeys.agentFile(resolvedId.value, resolvedFileName.value),
    ),
    queryFn: ({ signal }) =>
      fetchAgentFile(resolvedId.value, resolvedFileName.value, signal),
    enabled: queryEnabled,
  })
}
