import type { ProductsListQueryResult, NearestOffersQueryResult } from '~/composables/graphql/public/geo-generated'
import {
  ProductsListDocument,
  GetNodeDocument,
  NearestOffersDocument
} from '~/composables/graphql/public/geo-generated'
import {
  GetSupplierOffersDocument
} from '~/composables/graphql/public/exchange-generated'

// Type from codegen
type ProductItem = NonNullable<NonNullable<ProductsListQueryResult['products_list']>[number]>
type OfferItem = NonNullable<NonNullable<NearestOffersQueryResult['nearest_offers']>[number]>

// Product aggregated from offers
interface AggregatedProduct {
  uuid: string
  name: string | null | undefined
  offersCount: number
}

// Shared state
const items = ref<ProductItem[]>([])
const isLoading = ref(false)
const isLoadingMore = ref(false)
const isInitialized = ref(false)

// Filter state
const filterSupplierUuid = ref<string | null>(null)
const filterHubUuid = ref<string | null>(null)
const filterBounds = ref<{ west: number; south: number; east: number; north: number } | null>(null)

export function useCatalogProducts() {
  const { execute } = useGraphQL()

  // Products don't have server-side pagination yet, so we load all at once
  const canLoadMore = computed(() => false)

  const fetchProducts = async () => {
    if (isLoading.value) return
    isLoading.value = true
    try {
      let data

      if (filterSupplierUuid.value) {
        // Products from specific supplier - get offers directly (no geo radius)
        const offersData = await execute(
          GetSupplierOffersDocument,
          { teamUuid: filterSupplierUuid.value },
          'public',
          'exchange'
        )
        const productsMap = new Map<string, AggregatedProduct>()
        offersData?.getOffers?.forEach((offer) => {
          if (!offer?.productUuid) return
          if (!productsMap.has(offer.productUuid)) {
            productsMap.set(offer.productUuid, {
              uuid: offer.productUuid,
              name: offer.productName,
              offersCount: 0
            })
          }
          productsMap.get(offer.productUuid)!.offersCount++
        })
        items.value = Array.from(productsMap.values()) as ProductItem[]
      } else if (filterHubUuid.value) {
        // Products near hub - get hub coordinates first
        const hubData = await execute(
          GetNodeDocument,
          { uuid: filterHubUuid.value },
          'public',
          'geo'
        )
        const hub = hubData?.node

        if (!hub?.latitude || !hub?.longitude) {
          console.warn('Hub has no coordinates')
          items.value = []
        } else {
          // Get offers by graph from hub and group by product
          const offersData = await execute(
            NearestOffersDocument,
            {
              lat: hub.latitude,
              lon: hub.longitude,
              hubUuid: filterHubUuid.value,
              limit: 500
            },
            'public',
            'geo'
          )

          // Group offers by product
          const productsMap = new Map<string, AggregatedProduct>()
          offersData?.nearest_offers?.forEach((offer) => {
            if (!offer?.product_uuid) return
            if (!productsMap.has(offer.product_uuid)) {
              productsMap.set(offer.product_uuid, {
                uuid: offer.product_uuid,
                name: offer.product_name,
                offersCount: 0
              })
            }
            productsMap.get(offer.product_uuid)!.offersCount++
          })
          items.value = Array.from(productsMap.values()) as ProductItem[]
        }
      } else {
        // All products from graph
        data = await execute(
          ProductsListDocument,
          {
            limit: 500,
            ...(filterBounds.value && {
              west: filterBounds.value.west,
              south: filterBounds.value.south,
              east: filterBounds.value.east,
              north: filterBounds.value.north
            })
          },
          'public',
          'geo'
        )
        items.value = (data?.products_list || []).filter((p): p is ProductItem => p !== null)
      }

      isInitialized.value = true
    } finally {
      isLoading.value = false
    }
  }

  const loadMore = async () => {
    // No-op: products don't support pagination yet
  }

  const init = async () => {
    if (!isInitialized.value && items.value.length === 0) {
      await fetchProducts()
    }
  }

  // Filter setters
  const setSupplierFilter = (uuid: string | null) => {
    if (filterSupplierUuid.value !== uuid) {
      filterSupplierUuid.value = uuid
      filterHubUuid.value = null // clear other filter
      isInitialized.value = false
      fetchProducts()
    }
  }

  const setHubFilter = (uuid: string | null) => {
    if (filterHubUuid.value !== uuid) {
      filterHubUuid.value = uuid
      filterSupplierUuid.value = null // clear other filter
      isInitialized.value = false
      fetchProducts()
    }
  }

  const clearFilters = () => {
    if (filterSupplierUuid.value || filterHubUuid.value) {
      filterSupplierUuid.value = null
      filterHubUuid.value = null
      isInitialized.value = false
      fetchProducts()
    }
  }

  // Products are filtered by offer locations within bounds
  const setBoundsFilter = (bounds: { west: number; south: number; east: number; north: number } | null) => {
    // Early return if bounds haven't changed
    const prev = filterBounds.value
    const same = prev === bounds || (
      prev && bounds &&
      prev.west === bounds.west &&
      prev.south === bounds.south &&
      prev.east === bounds.east &&
      prev.north === bounds.north
    )
    if (same) return

    filterBounds.value = bounds
    if (isInitialized.value) {
      fetchProducts()
    }
  }

  return {
    items,
    isLoading,
    isLoadingMore,
    isInitialized,
    canLoadMore,
    fetchProducts,
    loadMore,
    init,
    setSupplierFilter,
    setHubFilter,
    clearFilters,
    setBoundsFilter
  }
}
