import { ref } from 'vue'
import { defineStore } from 'pinia'

export interface MediaSet {
  id: number
  name: string
  description: string
  iconImageUrl: string
  rootElementId: number | null
}

export interface CreateMediaSetInput {
  name: string
  description: string
  iconImage: File
}

export interface UpdateMediaSetInput {
  id: number
  name: string
  description: string
  iconImage: File | null
}

interface SetsResponse {
  sets: MediaSet[]
}

interface SetResponse {
  set: MediaSet
}

interface ErrorResponse {
  error?: string
}

const API_BASE_URL = import.meta.env.VITE_API_BASE_URL as string

export const useMediaSetsStore = defineStore('mediaSets', () => {
  const sets = ref<MediaSet[]>([])
  const isLoading = ref(false)
  const error = ref<string | null>(null)
  const isCreating = ref(false)
  const isUpdating = ref(false)
  const isDeleting = ref(false)
  const createError = ref<string | null>(null)
  const updateError = ref<string | null>(null)
  const deleteError = ref<string | null>(null)

  async function fetchSets(): Promise<void> {
    error.value = null
    isLoading.value = true

    try {
      const response = await fetch(`${API_BASE_URL}/api/sets`)

      if (!response.ok) {
        sets.value = []
        error.value = 'Could not load media sets'
        return
      }

      const data: SetsResponse = await response.json()
      sets.value = data.sets
    } catch {
      sets.value = []
      error.value = 'Network error - could not load media sets'
    } finally {
      isLoading.value = false
    }
  }

  async function createSet(
    input: CreateMediaSetInput,
  ): Promise<MediaSet | null> {
    createError.value = null
    isCreating.value = true

    const formData = new FormData()
    formData.append('name', input.name)
    formData.append('description', input.description)
    formData.append('iconImage', input.iconImage)

    try {
      const response = await fetch(`${API_BASE_URL}/api/sets`, {
        method: 'POST',
        credentials: 'include',
        body: formData,
      })

      if (!response.ok) {
        const data: ErrorResponse = await response.json()
        createError.value = data.error ?? 'Could not create media set'
        return null
      }

      const data: SetResponse = await response.json()
      sets.value = [...sets.value, data.set]

      return data.set
    } catch {
      createError.value = 'Network error - could not create media set'
      return null
    } finally {
      isCreating.value = false
    }
  }

  async function updateSet(
    input: UpdateMediaSetInput,
  ): Promise<MediaSet | null> {
    updateError.value = null
    isUpdating.value = true

    const formData = new FormData()
    formData.append('name', input.name)
    formData.append('description', input.description)
    if (input.iconImage !== null) {
      formData.append('iconImage', input.iconImage)
    }

    try {
      const encodedSetId = encodeURIComponent(input.id.toString())
      const response = await fetch(
        `${API_BASE_URL}/api/sets/${encodedSetId}/update`,
        {
          method: 'POST',
          credentials: 'include',
          body: formData,
        },
      )

      if (!response.ok) {
        updateError.value = await errorMessage(
          response,
          'Could not update media set',
        )
        return null
      }

      const data: SetResponse = await response.json()
      sets.value = sets.value.map((mediaSet) => {
        if (mediaSet.id === data.set.id) {
          return data.set
        }

        return mediaSet
      })

      return data.set
    } catch {
      updateError.value = 'Network error - could not update media set'
      return null
    } finally {
      isUpdating.value = false
    }
  }

  async function deleteSet(setId: number): Promise<boolean> {
    deleteError.value = null
    isDeleting.value = true

    try {
      const encodedSetId = encodeURIComponent(setId.toString())
      const response = await fetch(`${API_BASE_URL}/api/sets/${encodedSetId}`, {
        method: 'DELETE',
        credentials: 'include',
      })

      if (!response.ok) {
        deleteError.value = await errorMessage(
          response,
          'Could not delete media set',
        )
        return false
      }

      sets.value = sets.value.filter((mediaSet) => {
        return mediaSet.id !== setId
      })
      return true
    } catch {
      deleteError.value = 'Network error - could not delete media set'
      return false
    } finally {
      isDeleting.value = false
    }
  }

  async function errorMessage(
    response: Response,
    fallbackMessage: string,
  ): Promise<string> {
    try {
      const data: ErrorResponse = await response.json()
      return data.error ?? fallbackMessage
    } catch {
      return fallbackMessage
    }
  }

  return {
    sets,
    isLoading,
    error,
    isCreating,
    isUpdating,
    isDeleting,
    createError,
    updateError,
    deleteError,
    fetchSets,
    createSet,
    updateSet,
    deleteSet,
  }
})
