import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { z } from 'zod'
import { api, getCSRFToken } from '../client'
import type { Repository, TreeEntry } from '../../types/api'

const fileDiffSchema = z.object({
  path: z.string(),
  oldPath: z.string().optional(),
  additions: z.number(),
  deletions: z.number(),
  patch: z.string(),
})

const fileDiffsSchema = z.array(fileDiffSchema)

const repositorySchema = z.object({
  id: z.number(),
  ownerId: z.number(),
  ownerName: z.string(),
  isEmpty: z.boolean(),
  size: z.number().default(0),
  avatarUrl: z.string().default(''),
  name: z.string(),
  description: z.string(),
  isPrivate: z.boolean(),
  defaultBranch: z.string(),
  createdAt: z.string(),
  updatedAt: z.string(),
})

const repositoriesSchema = z.array(repositorySchema)

const treeEntrySchema = z.object({
  mode: z.string(),
  type: z.enum(['blob', 'tree']),
  hash: z.string(),
  name: z.string(),
  size: z.number().default(0),
  commitHash: z.string().default(''),
  commitMsg: z.string().default(''),
  commitDate: z.string().default(''),
})

const treeSchema = z.array(treeEntrySchema)

const blobSchema = z.object({
  content: z.string(),
  path: z.string(),
  ref: z.string(),
})

const branchSchema = z.object({ name: z.string() })
const branchesSchema = z.array(branchSchema)

export function useRepos() {
  return useQuery({
    queryKey: ['repos'],
    queryFn: () => api.get<Repository[]>('/api/v1/repos', repositoriesSchema),
  })
}

export function useRepo(owner: string, name: string) {
  return useQuery({
    queryKey: ['repos', owner, name],
    queryFn: () =>
      api.get<Repository>(`/api/v1/repos/${owner}/${name}`, repositorySchema),
    enabled: Boolean(owner && name),
  })
}

export function useRepoTree(owner: string, name: string, ref: string, path = '') {
  return useQuery({
    queryKey: ['repos', owner, name, 'tree', ref, path],
    queryFn: () =>
      api.get<TreeEntry[]>(
        `/api/v1/repos/${owner}/${name}/tree?ref=${ref}&path=${path}`,
        treeSchema,
      ),
    enabled: Boolean(owner && name && ref),
  })
}

export function useRepoDiff(owner: string, name: string, base: string, head: string) {
  return useQuery({
    queryKey: ['repos', owner, name, 'diff', base, head],
    queryFn: () =>
      api.get(`/api/v1/repos/${owner}/${name}/diff?base=${base}&head=${head}`, fileDiffsSchema),
    enabled: Boolean(owner && name && base && head),
  })
}

export function useUpdateRepo(owner: string, name: string) {
  const queryClient = useQueryClient()
  return useMutation({
    mutationFn: (data: { name?: string; description?: string; isPrivate?: boolean; defaultBranch?: string }) =>
      api.patch<Repository>(
        `/api/v1/repos/${owner}/${name}`,
        repositorySchema,
        data,
      ),
    onSuccess: (updated) => {
      queryClient.invalidateQueries({ queryKey: ['repos'] })
      queryClient.setQueryData(['repos', updated.ownerName, updated.name], updated)
    },
  })
}

export function useUploadRepoAvatar(owner: string, name: string) {
  const queryClient = useQueryClient()
  return useMutation({
    mutationFn: async (file: File) => {
      const token = await getCSRFToken()
      const formData = new FormData()
      formData.append('avatar', file)
      const res = await fetch(`/api/v1/repos/${owner}/${name}/avatar`, {
        method: 'POST',
        credentials: 'include',
        headers: { 'X-CSRF-Token': token },
        body: formData,
      })
      if (!res.ok) {
        const body = await res.json().catch(() => ({ error: 'Upload failed' }))
        throw new Error(body.error ?? 'Upload failed')
      }
      return res.json() as Promise<{ avatarUrl: string }>
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['repos', owner, name] })
    },
  })
}

export function useDeleteRepo(owner: string, name: string) {
  const queryClient = useQueryClient()
  return useMutation({
    mutationFn: () => api.delete(`/api/v1/repos/${owner}/${name}`, z.any()),
    onSuccess: () => queryClient.invalidateQueries({ queryKey: ['repos'] }),
  })
}

export function useRepoBranches(owner: string, name: string) {
  return useQuery({
    queryKey: ['repos', owner, name, 'branches'],
    queryFn: () =>
      api.get<{ name: string }[]>(`/api/v1/repos/${owner}/${name}/branches`, branchesSchema),
    enabled: Boolean(owner && name),
  })
}

export function useRepoBlob(owner: string, name: string, ref: string, path: string) {
  return useQuery({
    queryKey: ['repos', owner, name, 'blob', ref, path],
    queryFn: () =>
      api.get<{ content: string; path: string; ref: string }>(
        `/api/v1/repos/${owner}/${name}/blob?ref=${encodeURIComponent(ref)}&path=${encodeURIComponent(path)}`,
        blobSchema,
      ),
    enabled: Boolean(owner && name && ref && path),
  })
}

export function useUpdateBlob(owner: string, name: string) {
  const queryClient = useQueryClient()
  return useMutation({
    mutationFn: (data: { path: string; content: string; message: string; branch: string }) =>
      api.put<{ status: string }>(
        `/api/v1/repos/${owner}/${name}/blob`,
        z.object({ status: z.string() }),
        data,
      ),
    onSuccess: (_data, vars) => {
      queryClient.invalidateQueries({ queryKey: ['repos', owner, name, 'blob', vars.branch, vars.path] })
      queryClient.invalidateQueries({ queryKey: ['repos', owner, name, 'tree'] })
      queryClient.invalidateQueries({ queryKey: ['repos', owner, name, 'commits'] })
    },
  })
}

export function useCreateRepo() {
  const queryClient = useQueryClient()
  return useMutation({
    mutationFn: (data: {
      name: string
      description?: string
      isPrivate?: boolean
      defaultBranch?: string
      initReadme?: 'none' | 'blank' | 'tutorial'
      initGitignore?: boolean
    }) => api.post<Repository>('/api/v1/repos', repositorySchema, data),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['repos'] })
    },
  })
}

export function useImportRepo() {
  const queryClient = useQueryClient()
  return useMutation({
    mutationFn: (data: {
      url: string
      name: string
      description?: string
      isPrivate?: boolean
      authUser?: string
      authPass?: string
    }) => api.post<Repository>('/api/v1/repos/import', repositorySchema, data),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['repos'] })
    },
  })
}
