import {
  apiTemplateToTemplate,
  apiTemplateWithAttributesToTemplate,
  copyTemplateApi,
  CopyTemplateInput,
  createTemplateApi,
  deleteTemplateApi,
  getTemplateApi,
  getTemplatesApi,
  patchTemplateApi,
  templateToCreateInput,
  templateToPatchInput,
} from '@/entitiy/template/api/templateApiService'
import { Template } from '@/type/template'
import {
  QueryClient,
  useMutation,
  useQuery,
  useQueryClient,
} from '@tanstack/react-query'
import React, { createContext, ReactNode, useContext, useMemo } from 'react'

export const queryClient = new QueryClient()

interface TemplateContextType {
  templates: Template[]
  isLoading: boolean
  error: Error | null
  refetchTemplates: () => void
  addTemplate: (
    data: Omit<Template, 'id' | 'createdAt' | 'updatedAt'>
  ) => Promise<Template>
  updateTemplate: (
    template: Template,
    attributes?: Array<{ attributeId: string; value: string | null }>
  ) => Promise<Template>
  deleteTemplate: (id: string) => Promise<string>
  copyTemplate: (
    templateId: string,
    newName: string,
    newDescription?: string,
    attributes?: Array<{ attributeId: string; value: string | null }>
  ) => Promise<Template>
}

const TemplateContext = createContext<TemplateContextType | undefined>(
  undefined
)

export const TemplateProvider: React.FC<{ children: ReactNode }> = ({
  children,
}) => {
  const client = useQueryClient()

  const {
    data: templates = [],
    status: listStatus,
    error: errorList,
    refetch,
  } = useQuery<Template[], Error>({
    queryKey: ['templates'],
    queryFn: async () => {
      const { templates: apiList } = await getTemplatesApi()
      return apiList.map(apiTemplateWithAttributesToTemplate)
    },
  })

  const createMutation = useMutation({
    mutationFn: async (
      newTemplate: Omit<Template, 'id' | 'createdAt' | 'updatedAt'>
    ) => {
      const input = templateToCreateInput(newTemplate)
      const { id } = await createTemplateApi(input)
      const { template: apiTpl } = await getTemplateApi(id)
      if (!apiTpl) throw new Error('Template not found')
      return apiTemplateToTemplate(apiTpl)
    },
    onSuccess: () => {
      client.invalidateQueries({ queryKey: ['templates'] })
    },
  })

  const updateMutation = useMutation({
    mutationFn: async ({
      template,
      attributes,
    }: {
      template: Template
      attributes?: Array<{ attributeId: string; value: string | null }>
    }) => {
      const input = templateToPatchInput(template)
      if (attributes) {
        input.attributes = attributes.map(attr => ({
          attribute_id: attr.attributeId,
          value: attr.value,
        }))
      }
      await patchTemplateApi(template.id, input)
      return template
    },
    onSuccess: updated => {
      client.setQueryData<Template[]>(
        ['templates'],
        old =>
          old?.map((t: Template) => (t.id === updated.id ? updated : t)) ?? []
      )
    },
  })

  const deleteMutation = useMutation({
    mutationFn: async (id: string) => {
      const result = await deleteTemplateApi(id)
      if (!result.success) {
        throw new Error('Не удалось удалить шаблон')
      }
      return id
    },
    onSuccess: id => {
      client.setQueryData<Template[]>(
        ['templates'],
        old => old?.filter((t: Template) => t.id !== id) ?? []
      )
    },
  })

  const copyMutation = useMutation({
    mutationFn: async ({
      templateId,
      newName,
      newDescription,
      attributes,
    }: {
      templateId: string
      newName: string
      newDescription?: string
      attributes?: Array<{ attributeId: string; value: string | null }>
    }) => {
      const input: CopyTemplateInput = {
        template_id: templateId,
        new_name: newName,
        new_description: newDescription || null,
        new_attributes:
          attributes?.map(attr => ({
            attribute_id: attr.attributeId,
            value: attr.value,
          })) || null,
      }
      const { new_template_id } = await copyTemplateApi(input)
      const { template: apiTpl } = await getTemplateApi(new_template_id)
      if (!apiTpl) throw new Error('Copied template not found')
      return apiTemplateToTemplate(apiTpl)
    },
    onSuccess: () => {
      client.invalidateQueries({ queryKey: ['templates'] })
    },
  })

  const value = useMemo(
    () => ({
      templates,
      isLoading:
        listStatus === 'pending' ||
        createMutation.status === 'pending' ||
        updateMutation.status === 'pending' ||
        deleteMutation.status === 'pending' ||
        copyMutation.status === 'pending',
      error:
        (errorList as Error) ||
        (createMutation.error as Error) ||
        (updateMutation.error as Error) ||
        (deleteMutation.error as Error) ||
        (copyMutation.error as Error) ||
        null,
      refetchTemplates: refetch,
      addTemplate: createMutation.mutateAsync,
      updateTemplate: async (
        template: Template,
        attributes?: Array<{ attributeId: string; value: string | null }>
      ) =>
        updateMutation.mutateAsync({
          template,
          attributes,
        }),
      deleteTemplate: deleteMutation.mutateAsync,
      copyTemplate: async (
        templateId: string,
        newName: string,
        newDescription?: string,
        attributes?: Array<{ attributeId: string; value: string | null }>
      ) =>
        copyMutation.mutateAsync({
          templateId,
          newName,
          newDescription,
          attributes,
        }),
    }),
    [
      templates,
      listStatus,
      errorList,
      createMutation.status,
      createMutation.error,
      updateMutation.status,
      updateMutation.error,
      deleteMutation.status,
      deleteMutation.error,
      copyMutation.status,
      copyMutation.error,
      refetch,
      createMutation.mutateAsync,
      updateMutation.mutateAsync,
      deleteMutation.mutateAsync,
      copyMutation.mutateAsync,
    ]
  )

  return (
    <TemplateContext.Provider value={value}>
      {children}
    </TemplateContext.Provider>
  )
}

export const useTemplateContext = (): TemplateContextType => {
  const ctx = useContext(TemplateContext)
  if (!ctx)
    throw new Error('useTemplateContext must be used within TemplateProvider')
  return ctx
}

export default TemplateProvider
