import type { InfoEntityType } from './useCatalogSearch'
import type {
  GetNodeQueryResult,
  NearestHubsQueryResult,
  NearestOffersQueryResult
} from '~/composables/graphql/public/geo-generated'
import {
  GetNodeDocument,
  NearestOffersDocument,
  NearestHubsDocument
} from '~/composables/graphql/public/geo-generated'
import type {
  GetOfferQueryResult,
  GetSupplierProfileQueryResult
} from '~/composables/graphql/public/exchange-generated'
import {
  GetOfferDocument,
  GetSupplierProfileDocument,
  GetSupplierOffersDocument
} from '~/composables/graphql/public/exchange-generated'

// Types from codegen
type NodeEntity = NonNullable<GetNodeQueryResult['node']>
type OfferEntity = NonNullable<GetOfferQueryResult['getOffer']>
type SupplierProfile = NonNullable<GetSupplierProfileQueryResult['getSupplierProfile']>
type HubItem = NonNullable<NonNullable<NearestHubsQueryResult['nearest_hubs']>[number]>
type OfferItem = NonNullable<NonNullable<NearestOffersQueryResult['nearest_offers']>[number]>

// Product type (aggregated from offers)
export interface InfoProductItem {
  uuid: string
  name: string
  offersCount?: number
}

// Re-export types for InfoPanel
export type InfoHubItem = HubItem
export type InfoSupplierItem = SupplierProfile
export type InfoOfferItem = OfferItem

// Extended entity type with all known fields (NO index signature!)
export interface InfoEntity {
  uuid?: string | null
  name?: string | null
  // Node coordinates
  latitude?: number | null
  longitude?: number | null
  // Location fields
  address?: string | null
  city?: string | null
  country?: string | null
  // Offer coordinates (different field names)
  locationLatitude?: number | null
  locationLongitude?: number | null
  locationUuid?: string | null
  locationName?: string | null
  // Offer fields
  productUuid?: string | null
  productName?: string | null
  teamUuid?: string | null
  teamName?: string | null
  pricePerUnit?: number | string | null
  currency?: string | null
  unit?: string | null
  // Enriched field from supplier profile
  supplierName?: string | null
  // KYC profile reference
  kycProfileUuid?: string | null
}

// Helper to get coordinates from entity (handles both node and offer patterns)
function getEntityCoords(e: InfoEntity | null): { lat: number; lon: number } | null {
  if (!e) return null
  // Try offer coords first (locationLatitude/locationLongitude)
  const lat = e.locationLatitude ?? e.latitude
  const lon = e.locationLongitude ?? e.longitude
  if (lat != null && lon != null) {
    return { lat, lon }
  }
  return null
}

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

  // State with proper types
  const entity = ref<InfoEntity | null>(null)
  const entityType = ref<InfoEntityType | null>(null)
  const relatedProducts = ref<InfoProductItem[]>([])
  const relatedHubs = ref<HubItem[]>([])
  const relatedSuppliers = ref<SupplierProfile[]>([])
  const relatedOffers = ref<OfferItem[]>([])
  const selectedProduct = ref<string | null>(null)
  const activeTab = ref<string>('products')
  const isLoading = ref(false)

  // Separate loading states for each tab
  const isLoadingProducts = ref(false)
  const isLoadingHubs = ref(false)
  const isLoadingSuppliers = ref(false)
  const isLoadingOffers = ref(false)

  // Load hub info: hub details + products + suppliers (in parallel)
  const loadHubInfo = async (uuid: string) => {
    try {
      // Load hub node details
      const nodeData = await execute(GetNodeDocument, { uuid }, 'public', 'geo')
      entity.value = nodeData?.node ?? null

      const coords = getEntityCoords(entity.value)
      if (!coords) {
        console.warn('Hub has no coordinates')
        return
      }

      // Set default active tab to offers (first step shows products)
      activeTab.value = 'offers'

      // Load products AND suppliers in parallel
      isLoadingProducts.value = true
      isLoadingSuppliers.value = true

      // Load products (offers grouped by product) and extract suppliers
      execute(
        NearestOffersDocument,
        {
          lat: coords.lat,
          lon: coords.lon,
          hubUuid: uuid,
          limit: 500
        },
        'public',
        'geo'
      ).then(offersData => {
        // Group offers by product
        const productsMap = new Map<string, InfoProductItem>()
        const suppliersMap = new Map<string, { uuid: string; name: string; latitude?: number | null; longitude?: number | null }>()

        offersData?.nearest_offers?.forEach(offer => {
          if (!offer) return
          // Products
          if (offer.product_uuid && offer.product_name) {
            const existing = productsMap.get(offer.product_uuid)
            if (existing) {
              existing.offersCount = (existing.offersCount || 0) + 1
            } else {
              productsMap.set(offer.product_uuid, {
                uuid: offer.product_uuid,
                name: offer.product_name,
                offersCount: 1
              })
            }
          }

          // Suppliers (extract from offers)
          if (offer.supplier_uuid && !suppliersMap.has(offer.supplier_uuid)) {
            suppliersMap.set(offer.supplier_uuid, {
              uuid: offer.supplier_uuid,
              name: offer.supplier_name || 'Supplier',
              latitude: offer.latitude,
              longitude: offer.longitude
            })
          }
        })

        relatedProducts.value = Array.from(productsMap.values())

        // Load supplier profiles for detailed info
        const supplierUuids = Array.from(suppliersMap.keys()).slice(0, 12)
        if (supplierUuids.length > 0) {
          Promise.all(
            supplierUuids.map(supplierId =>
              execute(GetSupplierProfileDocument, { uuid: supplierId }, 'public', 'exchange')
                .then(data => data?.getSupplierProfile)
                .catch(() => suppliersMap.get(supplierId)) // Fallback to basic info
            )
          ).then(profiles => {
            relatedSuppliers.value = profiles.filter((p): p is SupplierProfile => p != null)
            isLoadingSuppliers.value = false
          })
        } else {
          relatedSuppliers.value = []
          isLoadingSuppliers.value = false
        }
      }).finally(() => {
        isLoadingProducts.value = false
      })
    } catch (error) {
      console.error('Error loading hub info:', error)
      isLoadingProducts.value = false
      isLoadingSuppliers.value = false
    }
  }

  // Load supplier info: supplier details + products + hubs (in parallel)
  const loadSupplierInfo = async (uuid: string) => {
    try {
      // Load supplier node details (might be geo node)
      const nodeData = await execute(GetNodeDocument, { uuid }, 'public', 'geo')
      entity.value = nodeData?.node ?? null

      // Also try to get supplier profile from exchange API for additional details
      try {
        const profileData = await execute(
          GetSupplierProfileDocument,
          { uuid },
          'public',
          'exchange'
        )
        if (profileData?.getSupplierProfile) {
          entity.value = { ...entity.value, ...profileData.getSupplierProfile }
        }
      } catch (e) {
        // Supplier profile might not exist, ignore
      }

      if (!entity.value?.latitude || !entity.value?.longitude) {
        console.warn('Supplier has no coordinates')
        return
      }

      // Set default active tab to offers (first step shows products)
      activeTab.value = 'offers'

      // Load products AND hubs in parallel
      isLoadingProducts.value = true
      isLoadingHubs.value = true

      // Load products from supplier offers (no geo radius)
      execute(
        GetSupplierOffersDocument,
        { teamUuid: uuid },
        'public',
        'exchange'
      ).then(offersData => {
        const productsMap = new Map<string, InfoProductItem>()
        offersData?.getOffers?.forEach(offer => {
          if (!offer?.productUuid || !offer.productName) return
          const existing = productsMap.get(offer.productUuid)
          if (existing) {
            existing.offersCount = (existing.offersCount || 0) + 1
          } else {
            productsMap.set(offer.productUuid, {
              uuid: offer.productUuid,
              name: offer.productName,
              offersCount: 1
            })
          }
        })
        relatedProducts.value = Array.from(productsMap.values())
      }).finally(() => {
        isLoadingProducts.value = false
      })

      // Load hubs near supplier
      execute(
        NearestHubsDocument,
        {
          lat: entity.value.latitude,
          lon: entity.value.longitude,
          sourceUuid: entity.value.uuid,
          limit: 12
        },
        'public',
        'geo'
      ).then(hubsData => {
        relatedHubs.value = (hubsData?.nearest_hubs || []).filter((h): h is HubItem => h !== null)
      }).finally(() => {
        isLoadingHubs.value = false
      })
    } catch (error) {
      console.error('Error loading supplier info:', error)
      isLoadingProducts.value = false
      isLoadingHubs.value = false
    }
  }

  // Load offer info: offer details + supplier + hubs
  const loadOfferInfo = async (uuid: string) => {
    try {
      // Load offer details from exchange API
      const offerData = await execute(GetOfferDocument, { uuid }, 'public', 'exchange')
      entity.value = offerData?.getOffer ?? null

      const coords = getEntityCoords(entity.value)
      if (!coords) {
        console.warn('Offer has no coordinates')
        return
      }

      // Set default active tab to hubs
      activeTab.value = 'hubs'

      // Set product as "related product" (single item)
      if (entity.value?.productUuid && entity.value?.productName) {
        relatedProducts.value = [
          {
            uuid: entity.value.productUuid,
            name: entity.value.productName
          }
        ]
      }

      // Load hubs near offer coordinates
      isLoadingHubs.value = true
      execute(
        NearestHubsDocument,
        {
          lat: coords.lat,
          lon: coords.lon,
          sourceUuid: entity.value?.uuid ?? null,
          limit: 12
        },
        'public',
        'geo'
      ).then(hubsData => {
        relatedHubs.value = (hubsData?.nearest_hubs || []).filter((h): h is HubItem => h !== null)
      }).finally(() => {
        isLoadingHubs.value = false
      })

      // If offer has supplier UUID, load supplier profile
      if (entity.value?.teamUuid) {
        isLoadingSuppliers.value = true
        execute(
          GetSupplierProfileDocument,
          { uuid: entity.value.teamUuid },
          'public',
          'exchange'
        ).then(supplierData => {
          const supplier = supplierData?.getSupplierProfile
          relatedSuppliers.value = supplier ? [supplier] : []
          // Enrich entity with supplier name for display
          if (supplier?.name && entity.value) {
            entity.value = { ...entity.value, supplierName: supplier.name }
          }
        }).catch(() => {
          // Supplier might not exist
        }).finally(() => {
          isLoadingSuppliers.value = false
        })
      }
    } catch (error) {
      console.error('Error loading offer info:', error)
    }
  }

  // Load offers for hub after product selection
  const loadOffersForHub = async (hubUuid: string, productUuid: string) => {
    try {
      const hub = entity.value
      if (!hub?.latitude || !hub?.longitude) {
        console.warn('Hub has no coordinates')
        return
      }

      // Load offers
      isLoadingOffers.value = true
      isLoadingSuppliers.value = true

      try {
        // Find offers near hub for this product WITH routes calculated on backend
        const offersData = await execute(
          NearestOffersDocument,
          {
            lat: hub.latitude,
            lon: hub.longitude,
            productUuid,
            hubUuid,  // Pass hubUuid to get routes calculated on backend
            limit: 12
          },
          'public',
          'geo'
        )

        // Offers already include routes from backend
        relatedOffers.value = (offersData?.nearest_offers || []).filter((o): o is OfferItem => o !== null)
        isLoadingOffers.value = false

        // Extract unique suppliers from offers (use supplier_uuid from offers)
        const supplierUuids = new Set<string>()
        relatedOffers.value.forEach(offer => {
          if (offer.supplier_uuid) {
            supplierUuids.add(offer.supplier_uuid)
          }
        })

        // Load supplier profiles (limit to 12)
        const suppliers: SupplierProfile[] = []
        for (const uuid of Array.from(supplierUuids).slice(0, 12)) {
          try {
            const supplierData = await execute(
              GetSupplierProfileDocument,
              { uuid },
              'public',
              'exchange'
            )
            if (supplierData?.getSupplierProfile) {
              suppliers.push(supplierData.getSupplierProfile)
            }
          } catch (e) {
            // Supplier might not exist
          }
        }
        relatedSuppliers.value = suppliers
      } finally {
        isLoadingOffers.value = false
        isLoadingSuppliers.value = false
      }
    } catch (error) {
      console.error('Error loading offers for hub:', error)
    }
  }

  // Load offers for supplier after product selection
  const loadOffersForSupplier = async (_supplierUuid: string, productUuid: string) => {
    try {
      const supplier = entity.value
      if (!supplier?.latitude || !supplier?.longitude) {
        console.warn('Supplier has no coordinates')
        return
      }

      isLoadingOffers.value = true
      isLoadingHubs.value = true

      try {
        let hubUuid: string | null = relatedHubs.value?.[0]?.uuid ?? null
        if (!hubUuid && supplier.uuid) {
          const hubsData = await execute(
            NearestHubsDocument,
            {
              lat: supplier.latitude,
              lon: supplier.longitude,
              sourceUuid: supplier.uuid,
              limit: 1
            },
            'public',
            'geo'
          )
          const hub = (hubsData?.nearest_hubs || []).find((h): h is HubItem => h !== null)
          if (hub?.uuid) {
            hubUuid = hub.uuid
            if (!relatedHubs.value.length) {
              relatedHubs.value = [hub]
            }
          }
        }

        // Find offers near supplier for this product
        const offersData = await execute(
          NearestOffersDocument,
          {
            lat: supplier.latitude,
            lon: supplier.longitude,
            productUuid,
            ...(hubUuid ? { hubUuid } : {}),
            limit: 12
          },
          'public',
          'geo'
        )

        relatedOffers.value = (offersData?.nearest_offers || []).filter((o): o is OfferItem => {
          if (!o) return false
          if (!supplier.uuid) return true
          return o.supplier_uuid === supplier.uuid
        })
        isLoadingOffers.value = false
      } finally {
        isLoadingOffers.value = false
        isLoadingHubs.value = false
      }
    } catch (error) {
      console.error('Error loading offers for supplier:', error)
    }
  }

  // Select product (triggers offers loading)
  const selectProduct = async (productUuid: string) => {
    selectedProduct.value = productUuid

    if (!entity.value) return

    // Use stored entity type instead of inferring from properties
    if (entityType.value === 'hub' && entity.value.uuid) {
      await loadOffersForHub(entity.value.uuid, productUuid)
      activeTab.value = 'offers'
    } else if (entityType.value === 'supplier' && entity.value.uuid) {
      await loadOffersForSupplier(entity.value.uuid, productUuid)
      activeTab.value = 'offers'
    }
  }

  // Set active tab
  const setActiveTab = (tab: string) => {
    activeTab.value = tab
  }

  // Main load method - dispatches to specific loaders
  const loadInfo = async (type: InfoEntityType, uuid: string) => {
    isLoading.value = true
    clearInfo() // Clear previous data
    entityType.value = type  // Store entity type

    try {
      if (type === 'hub') {
        await loadHubInfo(uuid)
      } else if (type === 'supplier') {
        await loadSupplierInfo(uuid)
      } else if (type === 'offer') {
        await loadOfferInfo(uuid)
      }
    } finally {
      isLoading.value = false
    }
  }

  // Clear all info data
  const clearInfo = () => {
    entity.value = null
    entityType.value = null
    relatedProducts.value = []
    relatedHubs.value = []
    relatedSuppliers.value = []
    relatedOffers.value = []
    selectedProduct.value = null
    activeTab.value = 'products'
    isLoadingProducts.value = false
    isLoadingHubs.value = false
    isLoadingSuppliers.value = false
    isLoadingOffers.value = false
  }

  return {
    // State
    entity,
    relatedProducts,
    relatedHubs,
    relatedSuppliers,
    relatedOffers,
    selectedProduct,
    activeTab,
    isLoading,
    isLoadingProducts,
    isLoadingHubs,
    isLoadingSuppliers,
    isLoadingOffers,

    // Actions
    loadInfo,
    selectProduct,
    setActiveTab,
    clearInfo
  }
}
