import { computed, toValue, type MaybeRefOrGetter } from 'vue'
import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
import type { components } from './generated/schema'
import { apiClient } from './client'
import { ApiProblem, asProblemDetails } from './contracts'
import { queryKeys } from './queryClient'
import { createMutationRequestContext } from '../services/mutationContext'

export type AgentCreateOptionsDto = components['schemas']['AgentCreateOptionsDto']
export type AgentProposalDto = components['schemas']['AgentProposalDto']
export type AgentProposalCollectionDto = components['schemas']['AgentProposalCollectionDto']
export type AgentProposalOperationDto = components['schemas']['AgentProposalOperationDto']
export type CreateAgentProposalRequest = components['schemas']['CreateAgentProposalRequest']
export type AgentProposalActionRequest = components['schemas']['AgentProposalActionRequest']

export interface AgentProposalListFilters {
  limit?: number
  cursor?: string
  status?: string
}

export interface AgentProposalActionInput extends AgentProposalActionRequest {
  proposalId: string
  clientRequestId?: string
}

export interface AgentProposalStatusMeta {
  label: string
  tone: 'neutral' | 'info' | 'success' | 'warning' | 'danger'
  description: string
}

const STATUS_META: Record<string, AgentProposalStatusMeta> = {
  draft: {
    label: 'Entwurf',
    tone: 'neutral',
    description: 'Der Vorschlag wurde noch nicht zur Freigabe eingereicht.',
  },
  awaiting_approval: {
    label: 'Freigabe offen',
    tone: 'warning',
    description: 'Ein Owner muss den Vorschlag prüfen und ausdrücklich freigeben.',
  },
  provisioning: {
    label: 'Wird provisioniert',
    tone: 'info',
    description: 'Nexus führt den freigegebenen OpenClaw-Auftrag aus und prüft das Ergebnis.',
  },
  ready: {
    label: 'Bereit',
    tone: 'success',
    description: 'Der Agent wurde in OpenClaw erstellt und erfolgreich zurückgelesen.',
  },
  partial: {
    label: 'Teilweise bereit',
    tone: 'warning',
    description: 'Der Agent existiert, aber mindestens ein abschließender Schritt ist fehlgeschlagen.',
  },
  failed: {
    label: 'Fehlgeschlagen',
    tone: 'danger',
    description: 'Die Provisionierung wurde beendet, ohne einen bestätigten Erfolgszustand zu erreichen.',
  },
  in_doubt: {
    label: 'Manuelle Prüfung',
    tone: 'danger',
    description: 'Nexus kann nicht sicher feststellen, ob OpenClaw die Mutation ausgeführt hat.',
  },
  rejected: {
    label: 'Abgelehnt',
    tone: 'neutral',
    description: 'Ein Owner hat den Vorschlag abgelehnt.',
  },
}

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

export function getAgentProposalStatusMeta(status: string): AgentProposalStatusMeta {
  return STATUS_META[status.toLowerCase()] ?? {
    label: status || 'Unbekannt',
    tone: 'neutral',
    description: 'OpenClaw hat einen noch nicht unterstützten Proposal-Status gemeldet.',
  }
}

export function canApproveAgentProposal(proposal: AgentProposalDto): boolean {
  return proposal.status === 'awaiting_approval'
}

export function canRejectAgentProposal(proposal: AgentProposalDto): boolean {
  return proposal.status === 'awaiting_approval'
}

export function canRetryAgentProposal(proposal: AgentProposalDto): boolean {
  return proposal.status === 'failed'
    || proposal.status === 'partial'
    || proposal.status === 'in_doubt'
}

export async function fetchAgentCreateOptions(signal?: AbortSignal): Promise<AgentCreateOptionsDto> {
  const { data, error, response } = await apiClient.GET('/api/v1/openclaw/agents/create-options', { signal })
  if (!response.ok || !data) fail(response, error, 'Agent-Optionen konnten nicht geladen werden')
  return data as AgentCreateOptionsDto
}

export async function fetchAgentProposals(
  filters: AgentProposalListFilters = {},
  signal?: AbortSignal,
): Promise<AgentProposalCollectionDto> {
  const { data, error, response } = await apiClient.GET('/api/v1/openclaw/agent-proposals', {
    params: {
      query: {
        ...(filters.limit ? { limit: filters.limit } : {}),
        ...(filters.cursor ? { cursor: filters.cursor } : {}),
        ...(filters.status ? { status: filters.status } : {}),
      },
    },
    signal,
  })
  if (!response.ok || !data) fail(response, error, 'Agent-Vorschläge konnten nicht geladen werden')
  return data as AgentProposalCollectionDto
}

export async function fetchAgentProposal(
  proposalId: string,
  signal?: AbortSignal,
): Promise<AgentProposalDto> {
  const { data, error, response } = await apiClient.GET('/api/v1/openclaw/agent-proposals/{id}', {
    params: { path: { id: proposalId } },
    signal,
  })
  if (!response.ok || !data) fail(response, error, 'Agent-Vorschlag konnte nicht geladen werden')
  return data as AgentProposalDto
}

export async function createAgentProposal(
  input: CreateAgentProposalRequest,
): Promise<AgentProposalOperationDto> {
  const clientRequestId = input.clientRequestId?.trim() || crypto.randomUUID()
  const requestContext = createMutationRequestContext('agent-proposal-create', clientRequestId)
  const { data, error, response } = await apiClient.POST('/api/v1/openclaw/agent-proposals', {
    headers: requestContext.headers,
    body: { ...input, clientRequestId },
  })
  if (!response.ok || !data) fail(response, error, 'Agent-Vorschlag konnte nicht erstellt werden')
  return data as AgentProposalOperationDto
}

async function mutateAgentProposal(
  action: 'approve' | 'reject' | 'retry',
  input: AgentProposalActionInput,
): Promise<AgentProposalOperationDto> {
  const requestContext = createMutationRequestContext(
    `agent-proposal-${action}`,
    input.clientRequestId,
  )
  const path = `/api/v1/openclaw/agent-proposals/{id}/${action}` as const
  const { data, error, response } = await apiClient.POST(path, {
    headers: requestContext.headers,
    params: { path: { id: input.proposalId } },
    body: {
      expectedRevision: input.expectedRevision,
      reason: input.reason?.trim() || null,
    },
  })
  if (!response.ok || !data) {
    const labels = {
      approve: 'Agent-Vorschlag konnte nicht freigegeben werden',
      reject: 'Agent-Vorschlag konnte nicht abgelehnt werden',
      retry: 'Provisionierung konnte nicht erneut gestartet werden',
    }
    fail(response, error, labels[action])
  }
  return data as AgentProposalOperationDto
}

export function approveAgentProposal(input: AgentProposalActionInput): Promise<AgentProposalOperationDto> {
  return mutateAgentProposal('approve', input)
}

export function rejectAgentProposal(input: AgentProposalActionInput): Promise<AgentProposalOperationDto> {
  return mutateAgentProposal('reject', input)
}

export function retryAgentProposal(input: AgentProposalActionInput): Promise<AgentProposalOperationDto> {
  return mutateAgentProposal('retry', input)
}

export function useAgentCreateOptionsQuery(
  enabled: MaybeRefOrGetter<boolean> = true,
) {
  return useQuery({
    queryKey: queryKeys.agentCreateOptions(),
    queryFn: ({ signal }) => fetchAgentCreateOptions(signal),
    enabled: computed(() => toValue(enabled)),
    staleTime: 30_000,
  })
}

export function useAgentProposalsQuery(
  filters: AgentProposalListFilters = {},
  enabled: MaybeRefOrGetter<boolean> = true,
) {
  const stableFilters = {
    ...(filters.limit ? { limit: filters.limit } : {}),
    ...(filters.cursor ? { cursor: filters.cursor } : {}),
    ...(filters.status ? { status: filters.status } : {}),
  }
  return useQuery({
    queryKey: ['openclaw', 'agent-proposals', stableFilters] as const,
    queryFn: ({ signal }) => fetchAgentProposals(stableFilters, signal),
    enabled: computed(() => toValue(enabled)),
  })
}

export function useAgentProposalQuery(
  proposalId: MaybeRefOrGetter<string>,
  enabled: MaybeRefOrGetter<boolean> = true,
) {
  const resolvedId = computed(() => toValue(proposalId).trim())
  return useQuery({
    queryKey: computed(() => queryKeys.agentProposal(resolvedId.value)),
    queryFn: ({ signal }) => fetchAgentProposal(resolvedId.value, signal),
    enabled: computed(() => Boolean(resolvedId.value) && toValue(enabled)),
    refetchInterval(query) {
      const proposal = query.state.data as AgentProposalDto | undefined
      return proposal?.status === 'provisioning' ? 2_000 : false
    },
  })
}

function useAgentProposalMutation(
  mutationFn: (input: AgentProposalActionInput) => Promise<AgentProposalOperationDto>,
) {
  const queryClient = useQueryClient()
  return useMutation({
    mutationFn,
    onSuccess: async operation => {
      if (operation.proposal) {
        queryClient.setQueryData(
          queryKeys.agentProposal(operation.proposal.id),
          operation.proposal,
        )
      }
      await Promise.all([
        queryClient.invalidateQueries({ queryKey: queryKeys.agentProposals() }),
        queryClient.invalidateQueries({ queryKey: queryKeys.openClawAgents() }),
      ])
    },
  })
}

export function useCreateAgentProposalMutation() {
  const queryClient = useQueryClient()
  return useMutation({
    mutationFn: createAgentProposal,
    onSuccess: async operation => {
      if (operation.proposal) {
        queryClient.setQueryData(
          queryKeys.agentProposal(operation.proposal.id),
          operation.proposal,
        )
      }
      await queryClient.invalidateQueries({ queryKey: queryKeys.agentProposals() })
    },
  })
}

export function useApproveAgentProposalMutation() {
  return useAgentProposalMutation(approveAgentProposal)
}

export function useRejectAgentProposalMutation() {
  return useAgentProposalMutation(rejectAgentProposal)
}

export function useRetryAgentProposalMutation() {
  return useAgentProposalMutation(retryAgentProposal)
}
