import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
import { computed, toValue, type MaybeRefOrGetter } from 'vue'
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 ProjectDto = components['schemas']['ProjectDto']
export type ProjectTaskDto = components['schemas']['ProjectTaskDto']

export interface CreateProjectInput {
  name: string
  description?: string | null
}

export interface UpdateProjectInput {
  name?: string | null
  description?: string | null
  status?: string | null
}

export async function fetchProjects(signal?: AbortSignal): Promise<ProjectDto[]> {
  const { data, error, response } = await apiClient.GET('/api/v1/projects', { signal })
  if (!response.ok || error || !data) {
    await throwApiProblem(response, 'Projekte konnten nicht geladen werden')
  }
  return data as ProjectDto[]
}

export async function fetchProject(id: string, signal?: AbortSignal): Promise<ProjectDto> {
  const { data, error, response } = await apiClient.GET('/api/v1/projects/{id}', {
    params: { path: { id } },
    signal,
  })
  if (!response.ok || error || !data) {
    await throwApiProblem(response, 'Projekt konnte nicht geladen werden')
  }
  return data as ProjectDto
}

async function createProject(input: CreateProjectInput): Promise<ProjectDto> {
  const { data, error, response } = await apiClient.POST('/api/v1/projects', {
    body: {
      name: input.name,
      description: input.description ?? null,
    },
  })
  if (!response.ok || error || !data) {
    await throwApiProblem(response, 'Projekt konnte nicht erstellt werden')
  }
  const project = data as ProjectDto
  reportOperationResult(project.operation, 'Projekt erstellt')
  return withoutProjectOperation(project)
}

export async function updateProject(id: string, input: UpdateProjectInput): Promise<ProjectDto> {
  const { data, error, response } = await apiClient.PATCH('/api/v1/projects/{id}', {
    params: { path: { id } },
    body: input as components['schemas']['UpdateProjectRequest'],
  })
  if (!response.ok || error || !data) {
    await throwApiProblem(response, 'Projekt konnte nicht gespeichert werden')
  }
  const project = data as ProjectDto
  reportOperationResult(project.operation, 'Projekt aktualisiert')
  return withoutProjectOperation(project)
}

function withoutProjectOperation(project: ProjectDto): ProjectDto {
  const { operation: _operation, ...persistedProject } = project
  return persistedProject
}

export async function fetchProjectTasks(
  id: string,
  signal?: AbortSignal,
): Promise<ProjectTaskDto[]> {
  const { data, error, response } = await apiClient.GET('/api/v1/projects/{id}/tasks', {
    params: { path: { id } },
    signal,
  })
  if (!response.ok || error || !data) {
    await throwApiProblem(response, 'Projektaufgaben konnten nicht geladen werden')
  }
  return data as ProjectTaskDto[]
}

export function useProjects() {
  const client = useQueryClient()
  const query = useQuery<ProjectDto[]>({
    queryKey: queryKeys.projects(),
    queryFn: ({ signal }) => fetchProjects(signal),
  })
  const createMutation = useMutation({
    mutationFn: createProject,
    onSuccess: project => {
      client.setQueryData<ProjectDto[]>(queryKeys.projects(), current =>
        current ? [project, ...current.filter(item => item.id !== project.id)] : [project])
      client.setQueryData(queryKeys.project(project.id), project)
    },
  })

  return {
    query,
    createProject: (input: CreateProjectInput) => createMutation.mutateAsync(input),
    creating: createMutation.isPending,
    createError: createMutation.error,
  }
}

export function useProject(id: MaybeRefOrGetter<string>) {
  const client = useQueryClient()
  const query = useQuery<ProjectDto>({
    queryKey: computed(() => queryKeys.project(toValue(id))),
    queryFn: ({ signal }) => fetchProject(toValue(id), signal),
    enabled: () => Boolean(toValue(id)),
  })
  const updateMutation = useMutation({
    mutationFn: (input: UpdateProjectInput) => updateProject(toValue(id), input),
    onSuccess: project => {
      client.setQueryData(queryKeys.project(project.id), project)
      client.setQueryData<ProjectDto[]>(queryKeys.projects(), current =>
        current?.map(item => item.id === project.id ? project : item))
    },
  })
  return {
    query,
    updateProject: (input: UpdateProjectInput) => updateMutation.mutateAsync(input),
    updating: updateMutation.isPending,
    updateError: updateMutation.error,
  }
}

export function useProjectTasks(id: MaybeRefOrGetter<string>) {
  return useQuery<ProjectTaskDto[]>({
    queryKey: computed(() => queryKeys.projectTasks(toValue(id))),
    queryFn: ({ signal }) => fetchProjectTasks(toValue(id), signal),
    enabled: () => Boolean(toValue(id)),
  })
}
