import { computed, toValue, type MaybeRefOrGetter } from 'vue'
import { useQuery } from '@tanstack/vue-query'
import type { paths } from './generated/schema'
import { apiClient } from './client'
import { ApiProblem, asProblemDetails } from './contracts'
import { queryClient, queryKeys } from './queryClient'
import type { AgentNodeData } from '../composables/useFlowLayout'

type JsonBody<TResponse> = TResponse extends {
  content: { 'application/json': infer TBody }
}
  ? TBody
  : never

type GeneratedOverview = JsonBody<
  paths['/api/v1/openclaw/overview']['get']['responses'][200]
>
type GeneratedCapabilities = JsonBody<
  paths['/api/v1/openclaw/capabilities']['get']['responses'][200]
>
type GeneratedAgents = JsonBody<
  paths['/api/v1/openclaw/agents']['get']['responses'][200]
>

export type OpenClawOverviewDto = GeneratedOverview
export type OpenClawCapabilitiesDto = GeneratedCapabilities
export type OpenClawAgentsDto = GeneratedAgents
type OpenClawAgentDto = OpenClawAgentsDto['items'][number]

function fail(response: Response, error: unknown, fallback: string): never {
  throw new ApiProblem(response.status, asProblemDetails(error, response.status), fallback)
}

export async function fetchOpenClawOverview(
  signal?: AbortSignal,
): Promise<OpenClawOverviewDto> {
  const { data, error, response } = await apiClient.GET('/api/v1/openclaw/overview', { signal })
  if (!response.ok || !data) fail(response, error, 'OpenClaw-Übersicht konnte nicht geladen werden')
  return data
}

export async function fetchOpenClawCapabilities(
  signal?: AbortSignal,
): Promise<OpenClawCapabilitiesDto> {
  const { data, error, response } = await apiClient.GET('/api/v1/openclaw/capabilities', { signal })
  if (!response.ok || !data) fail(response, error, 'OpenClaw-Capabilities konnten nicht geladen werden')
  return data
}

export async function fetchOpenClawAgents(
  signal?: AbortSignal,
): Promise<OpenClawAgentsDto> {
  const { data, error, response } = await apiClient.GET('/api/v1/openclaw/agents', { signal })
  if (!response.ok || !data) fail(response, error, 'OpenClaw-Agenten konnten nicht geladen werden')
  return data
}

export function openClawOverviewQueryOptions() {
  return {
    queryKey: queryKeys.openClawOverview(),
    queryFn: ({ signal }: { signal: AbortSignal }) => fetchOpenClawOverview(signal),
    staleTime: 15_000,
  } as const
}

export function openClawCapabilitiesQueryOptions() {
  return {
    queryKey: queryKeys.openClawCapabilities(),
    queryFn: ({ signal }: { signal: AbortSignal }) => fetchOpenClawCapabilities(signal),
    staleTime: 30_000,
  } as const
}

export function openClawAgentsQueryOptions() {
  return {
    queryKey: queryKeys.openClawAgents(),
    queryFn: ({ signal }: { signal: AbortSignal }) => fetchOpenClawAgents(signal),
    staleTime: 15_000,
  } as const
}

export function useOpenClawOverviewQuery(
  enabled: MaybeRefOrGetter<boolean> = true,
) {
  return useQuery({
    ...openClawOverviewQueryOptions(),
    enabled: computed(() => toValue(enabled)),
  })
}

export function useOpenClawCapabilitiesQuery(
  enabled: MaybeRefOrGetter<boolean> = true,
) {
  return useQuery({
    ...openClawCapabilitiesQueryOptions(),
    enabled: computed(() => toValue(enabled)),
  })
}

export function useOpenClawAgentsQuery(
  enabled: MaybeRefOrGetter<boolean> = true,
) {
  return useQuery({
    ...openClawAgentsQueryOptions(),
    enabled: computed(() => toValue(enabled)),
  })
}

export async function refreshOpenClawOverview(): Promise<OpenClawOverviewDto> {
  await queryClient.invalidateQueries({
    queryKey: queryKeys.openClawOverview(),
    refetchType: 'none',
  })
  return queryClient.fetchQuery({
    ...openClawOverviewQueryOptions(),
    staleTime: 0,
  })
}

export async function invalidateOpenClawRuntime(): Promise<void> {
  await queryClient.invalidateQueries({ queryKey: ['openclaw'] })
}

function agentStatus(
  agent: OpenClawAgentDto,
  hasActiveTask: boolean,
  hasActiveSession: boolean,
): AgentNodeData['status'] {
  const status = agent.status.trim().toLocaleLowerCase()
  if (['blocked', 'failed', 'error', 'unavailable'].includes(status)) return 'block'
  if (['thinking', 'planning'].includes(status)) return 'think'
  if (hasActiveTask || hasActiveSession || ['active', 'running', 'working'].includes(status)) return 'work'
  return 'idle'
}

function statusLabel(status: AgentNodeData['status']): string {
  if (status === 'work') return 'Arbeitet'
  if (status === 'think') return 'Plant'
  if (status === 'block') return 'Blockiert'
  return 'Bereit'
}

function avatarFor(id: string, name: string): string {
  if (id === 'iris') return 'IR'
  if (id === 'programmer' || id === 'developer') return '</>'
  return name.slice(0, 2).toUpperCase()
}

function formatTokenCount(value: number): string {
  if (value >= 1_000_000) return `${(value / 1_000_000).toFixed(value >= 10_000_000 ? 0 : 1)}M`
  if (value >= 1_000) return `${(value / 1_000).toFixed(value >= 100_000 ? 0 : 1)}k`
  return Math.max(0, Math.round(value)).toLocaleString('de-DE')
}

export function projectOpenClawAgentNodes(
  overview: OpenClawOverviewDto | null | undefined,
): AgentNodeData[] {
  if (!overview) return []

  return overview.agents.items.map(agent => {
    const task = overview.tasks.items.find(item =>
      item.agentId === agent.id && ['queued', 'running', 'active'].includes(item.status),
    )
    const session = overview.sessions.items.find(item =>
      item.agentId === agent.id && ['running', 'active'].includes(item.status),
    )
    const status = agentStatus(agent, Boolean(task), Boolean(session))
    const tokens = overview.sessions.items
      .filter(item => item.agentId === agent.id)
      .reduce((sum, item) => sum + Number(item.totalTokens ?? 0), 0)

    return {
      id: agent.id,
      name: agent.name,
      role: agent.id === 'iris' ? 'Chief of Staff' : 'OpenClaw Agent',
      roleBadge: agent.id === 'iris' ? 'badge-violet' : 'badge-slate',
      model: session?.model ?? agent.model ?? 'OpenClaw default',
      avatar: avatarFor(agent.id, agent.name),
      status,
      statusLabel: statusLabel(status),
      task: task?.title ?? session?.title ?? null,
      goal: task?.summary ?? agent.description ?? null,
      progress: task?.progress == null ? null : Number(task.progress),
      elapsed: null,
      next: null,
      tokens: tokens > 0 ? formatTokenCount(tokens) : null,
      cost: null,
      statusDetail: task?.summary ?? agent.description ?? null,
    }
  })
}

export function projectOpenClawModels(
  overview: OpenClawOverviewDto | null | undefined,
): Array<{ id: string; alias: string }> {
  return (overview?.models.items ?? [])
    .filter(model => model.available || model.configured)
    .map(model => ({ id: model.id, alias: model.name }))
}
