import { useEffect, useState, useCallback, useMemo } from 'react'
import {
  AddAffiliateProductResult,
  CommissionType,
  Product,
  useToast
} from '@komi-app/creator-ui'
import { Maybe } from 'types/utilities'
import debounce from 'lodash/debounce'
import {
  fetchProductsFromWayward,
  WaywardCategories as categories,
  generateWaywardLink
} from './service/wayward'
import { GetProductsQuery, GetProductsResponse } from '@komi-app/affiliate-sdk'

import {
  AffiliateProductCatalogScreen,
  AffiliateItemDetailModal,
  AddAffiliateProduct,
  AffiliateHowItWorks,
  AffiliateProduct as AffiliateProductAddProduct
} from '@komi-app/creator-ui'

import selfless from '@komi-app/selfless'
import { AffiliateNetworkType } from '@komi-app/profiles-sdk'
import { AffiliateItem } from '../../models/talent/talent-profile-module.model'
import { uploadImageWithCrop } from '../../utils/photo'
import { useIsOwner } from '../../hooks/user-hooks'
import { useHistory } from 'react-router-dom'

import { v4 as uuidv4 } from 'uuid'
import { useSelector } from 'react-redux'
import { selectUserData } from 'redux/User/selector'
import { FLAGS, getFeatureValue } from '@komi-app/flags-sdk'

export interface IProps {
  visible: boolean
  onChangeVisible: (value: boolean) => void
  onConfirmCallback: (
    product: AffiliateItem,
    result: AddAffiliateProductResult
  ) => void
}

export type ProductWithDetails = Product & {
  url?: string
  description?: string
  discountedPrice?: number
  trackingUrl?: string
  affiliateNetwork?: AffiliateNetworkType
  affiliateMetadata?: Record<string, any> & {
    asin: string
    commissionRate: number
    commissionType: CommissionType
  }
}

export interface ProfileAmazonTags {
  [profileId: string]: string
}

export function appendAmazonTag(
  profileId?: string,
  trackingUrl?: string,
  amazonTagsList?: ProfileAmazonTags
): Maybe<string> {
  const amzAssociateTag =
    profileId && amazonTagsList ? amazonTagsList[profileId] : null

  if (amzAssociateTag && trackingUrl) {
    const url = new URL(trackingUrl)
    url.searchParams.set('tag', amzAssociateTag)
    return url.toString()
  } else {
    return trackingUrl
  }
}

export function useSelf(props: IProps) {
  const { visible, onChangeVisible, onConfirmCallback } = props

  const [products, setProducts] = useState<ProductWithDetails[]>([])
  const [uiProducts, setUiProducts] = useState<ProductWithDetails[]>([])
  const [imageUploadError, setImageUploadError] = useState('')
  const [modalVisible, setModalVisible] = useState(visible)
  const [modalLoading, setModalLoading] = useState(false)
  const [currentPage, setCurrentPage] = useState(1)
  const [totalPages, setTotalPages] = useState(0)
  const [searchQuery, setSearchQuery] = useState('')
  const [selectedCategoryKey, setSelectedCategoryKey] = useState('')
  const [selectedProduct, setSelectedProduct] =
    useState<Maybe<ProductWithDetails>>(undefined)
  const [showAddProductModal, setShowAddProductModal] = useState(false)
  const [productToAdd, setProductToAdd] =
    useState<Maybe<ProductWithDetails>>(undefined)
  const [trackingUrl, setTrackingUrl] = useState<Maybe<string>>(undefined)
  const [trackingUrlError, setTrackingUrlError] =
    useState<Maybe<string>>(undefined)
  const [showProductDetailModal, setShowProductDetailModal] = useState(false)
  const [howItWorksModalVisible, setHowItWorksModalVisible] = useState(false)

  const isOwner = useIsOwner()
  const { createToast } = useToast()
  const router = useHistory()

  const user = useSelector(selectUserData)
  const profileId = user?.talentProfile?.id
  const amzTagsListJSON: ProfileAmazonTags = getFeatureValue(
    FLAGS.FEAT_SB_972_AFFILIATE_AMAZON_ASSOCIATE_TAG,
    {}
  )

  function handleHowItWorksClick() {
    setModalVisible(false)
    setHowItWorksModalVisible(true)
  }

  function handleHowItWorksPrevious() {
    setHowItWorksModalVisible(false)
    setModalVisible(true)
  }

  function handleSettingsClick() {
    if (isOwner) {
      router.push('/admin/settings/subscription')
    } else {
      createToast({
        text: 'You don’t have access to Billing & Payments settings, contact the owner of this Komi mini-site',
        semantic: 'warning',
        dismissible: true,
        durationMs: 5000
      })
    }
  }

  useEffect(() => {
    setModalVisible(visible)
    if (visible) {
      fetchProducts(currentPage, searchQuery, selectedCategoryKey)
    }
  }, [visible, currentPage])

  const debouncedFetchProducts = useCallback(
    debounce((page, searchTerm, categoryId) => {
      fetchProducts(page, searchTerm, categoryId)
    }, 500),
    []
  )

  useEffect(() => {
    if (visible) {
      debouncedFetchProducts(currentPage, searchQuery, selectedCategoryKey)
    }
  }, [searchQuery, currentPage, selectedCategoryKey])

  // Generates the affiliate link whenever the selected product changes
  useEffect(() => {
    if (productToAdd && !trackingUrl && !trackingUrlError && !modalLoading) {
      setModalLoading(true)

      getTrackingUrl(productToAdd)
        .then(url => {
          setTrackingUrl(url)
        })
        .catch(() => {
          setTrackingUrlError(
            "We couldn't generate an affiliate link at this time. Please try another product."
          )
        })
        .finally(() => {
          setModalLoading(false)
        })
    }
  }, [productToAdd, trackingUrl, trackingUrlError, modalLoading])

  const fetchProducts = async (
    page: number,
    searchTerm = '',
    categoryId = ''
  ) => {
    setModalLoading(true)
    try {
      const initialSearch: GetProductsQuery = {
        page,
        pageSize: 8,
        categoryId: categoryId === 'all' ? '' : categoryId,
        searchTerm
      }

      const catalogList: Maybe<GetProductsResponse> =
        await fetchProductsFromWayward(initialSearch)
      if (catalogList) {
        const productsWithDetails = convertToProducts(
          catalogList.items,
          AffiliateNetworkType.WAYWARD
        )
        const uiProductsWithDetails: ProductWithDetails[] =
          productsWithDetails.map(p => ({
            ...p,
            price: {
              amount: p.discountedPrice || p.price.amount,
              currency: p.price.currency
            }
          }))

        setProducts(productsWithDetails)
        setUiProducts(uiProductsWithDetails)
        setTotalPages(catalogList.totalPages)
      }
    } catch (error) {
      console.error('Error fetching products: ', error)
    } finally {
      setModalLoading(false)
    }
  }

  const handleSearchQueryChange = (query: string) => {
    setSearchQuery(query)
    setCurrentPage(1)
  }

  const handleCategoryChange = (categoryId: string) => {
    setSelectedCategoryKey(categoryId)
    setCurrentPage(1)
  }

  const handleProductDetails = (id: ProductWithDetails['id']) => {
    const product = products.find(p => p.id === id)

    setModalVisible(false)
    setSelectedProduct(product)
    setShowProductDetailModal(true)
  }

  function convertToProducts(
    dataArray: any[],
    network: AffiliateNetworkType
  ): ProductWithDetails[] {
    return dataArray.map(data => ({
      id: data.id,
      name: data.title,
      image: data.image,
      price: {
        amount: parseFloat(data.price),
        currency: data.currency
      },
      commissionRate: parseFloat(data.meta.commissionRate),
      commissionType: data.meta.commissionType as CommissionType,
      url: data.url,
      affiliateMetadata: data.meta,
      affiliateNetwork: network,
      description: data.meta.description,
      discountedPrice: data.discountedPrice,
      originalPrice: parseFloat(data.price)
    }))
  }

  // Converts the catalog product to the AffiliateItem we can store in the db
  const convertToAffiliateItem = (
    product: ProductWithDetails,
    trackingUrl: string
  ): AffiliateItem => ({
    id: uuidv4(),
    title: product.name,
    image: product.image,
    price: product.price.amount,
    currency: product.price.currency,
    url: product.url || trackingUrl,
    trackingUrl,
    affiliateMetadata: product.affiliateMetadata,
    affiliateNetwork: product.affiliateNetwork,
    discountedPrice: product.discountedPrice || product.price.amount
  })

  const handleNextPage = () => {
    if (currentPage < totalPages) {
      setCurrentPage(prevPage => prevPage + 1)
    }
  }

  const handlePreviousPage = () => {
    if (currentPage > 1) {
      setCurrentPage(prevPage => prevPage - 1)
    }
  }

  const handleProductDetailClose = () => {
    setModalVisible(true)
    setSelectedProduct(undefined)
  }

  const handleProductAdd = (id: ProductWithDetails['id']) => {
    const product = products.find(p => p.id === id)

    setProductToAdd(product)
    setModalVisible(false)
    setShowAddProductModal(true)
  }

  // Converts our internal product type to type expected by the AddAffiliateProduct modal
  // Do not store this!!
  const addModalProduct = useMemo((): Maybe<AffiliateProductAddProduct> => {
    if (!productToAdd) return undefined

    const amzTaggedURL = appendAmazonTag(
      profileId,
      trackingUrl,
      amzTagsListJSON
    )

    return {
      title: productToAdd.name,
      image: productToAdd.image,
      price: productToAdd.price.amount,
      discountedPrice: productToAdd.discountedPrice,
      currency: productToAdd.price.currency,
      url: productToAdd.url,
      trackingUrl: amzTaggedURL ? amzTaggedURL : trackingUrl,
      affiliateMetadata: productToAdd.affiliateMetadata
    }
  }, [productToAdd, trackingUrl])

  const handleAddAffiliateProductPrevious = () => {
    setTrackingUrl(undefined)
    setTrackingUrlError(undefined)
    setProductToAdd(undefined)
    setImageUploadError('')
    setShowAddProductModal(false)
    if (selectedProduct) setShowProductDetailModal(true)
    else setModalVisible(true)
  }

  const handleAddAffiliateProductClose = () => {
    setTrackingUrl(undefined)
    setTrackingUrlError(undefined)
    setProductToAdd(undefined)
    setShowAddProductModal(false)
    if (selectedProduct) setShowProductDetailModal(true)
    else setModalVisible(true)
  }

  const handleProductSubmit = () => {
    if (selectedProduct) {
      setProductToAdd(selectedProduct)
      setShowAddProductModal(true)
      setShowProductDetailModal(false)
    }
  }

  const getTrackingUrl = async (
    product: ProductWithDetails
  ): Promise<string> => {
    if (product.affiliateNetwork !== AffiliateNetworkType.WAYWARD) {
      throw new Error('Tracking URL not available for this network')
    }

    if (!product.affiliateMetadata || !product.affiliateMetadata.asin) {
      throw new Error('ASIN not available')
    }

    const asin = product.affiliateMetadata.asin

    const trackingUrl = await generateWaywardLink({ asin })

    if (!trackingUrl) {
      throw new Error('Failed to generate tracking URL')
    }

    return trackingUrl.link
  }

  const handleProductDone = async (
    productToAdd: ProductWithDetails,
    result: AddAffiliateProductResult
  ) => {
    if (!trackingUrl) return

    let image = productToAdd.image

    // We need to upload an image, don't save anything till we've uploaded it
    if (result.image && result.croppedAreaPixels) {
      setModalLoading(true)

      try {
        const uploadedImage = await uploadImageWithCrop(
          result.image,
          result.croppedAreaPixels
        )

        image = uploadedImage.url
      } catch (e: any) {
        setImageUploadError(e?.message || 'An unknown error occurred')
        setModalLoading(false)
        return
      }
    }

    onConfirmCallback(
      {
        ...convertToAffiliateItem(productToAdd, trackingUrl),
        image
      },
      result
    )

    setModalLoading(false)
    setShowAddProductModal(false)
  }

  return {
    products: uiProducts,
    modalVisible,
    imageUploadError,
    modalLoading,
    currentPage,
    totalPages,
    searchQuery,
    selectedCategoryKey,
    selectedProduct,
    showAddProductModal,
    productToAdd,
    addModalProduct,
    trackingUrlError,
    setModalVisible,
    handleSearchQueryChange,
    handleCategoryChange,
    handleProductDetails,
    handleNextPage,
    handlePreviousPage,
    handleProductAdd,
    handleAddAffiliateProductPrevious,
    handleProductSubmit,
    onChangeVisible,
    handleProductDone,
    setSelectedProduct,
    setShowAddProductModal,
    handleProductDetailClose,
    handleAddAffiliateProductClose,
    setShowProductDetailModal,
    showProductDetailModal,
    categories,
    howItWorksModalVisible,
    handleHowItWorksClick,
    handleHowItWorksPrevious,
    handleSettingsClick
  }
}

export const ui = ({
  products,
  modalVisible,
  modalLoading,
  currentPage,
  totalPages,
  searchQuery,
  selectedCategoryKey,
  selectedProduct,
  showAddProductModal,
  productToAdd,
  addModalProduct,
  imageUploadError,
  trackingUrlError,
  handleSearchQueryChange,
  handleCategoryChange,
  handleProductDetails,
  handleNextPage,
  handlePreviousPage,
  handleProductAdd,
  handleAddAffiliateProductPrevious,
  handleProductSubmit,
  onChangeVisible,
  handleProductDone,
  categories,
  showProductDetailModal,
  handleProductDetailClose,
  handleAddAffiliateProductClose,
  howItWorksModalVisible,
  handleHowItWorksClick,
  handleHowItWorksPrevious,
  handleSettingsClick
}: ReturnType<typeof useSelf>) => {
  return (
    <>
      <AffiliateProductCatalogScreen
        hasNextPage={currentPage < totalPages}
        hasPreviousPage={currentPage > 1}
        isLoading={modalLoading}
        onNextPage={handleNextPage}
        onPreviousPage={handlePreviousPage}
        onProductAdd={handleProductAdd}
        onProductDetails={handleProductDetails}
        onSearchQueryChange={handleSearchQueryChange}
        products={products}
        searchQuery={searchQuery}
        visible={modalVisible}
        onClose={() => onChangeVisible(false)}
        categories={[{ name: 'All', key: 'all' }, ...categories]}
        selectedCategoryKey={selectedCategoryKey}
        onCategoryChange={handleCategoryChange}
        onHowItWorksClick={handleHowItWorksClick}
      />
      {selectedProduct && showProductDetailModal && (
        <AffiliateItemDetailModal
          visible={true}
          disabled={false}
          imageUrl={selectedProduct.image}
          squared={true}
          heading="Add affiliate product"
          productName={selectedProduct.name}
          currency={selectedProduct.price.currency}
          price={
            selectedProduct.discountedPrice || selectedProduct.price.amount
          }
          commissionType={selectedProduct.commissionType}
          commissionRate={selectedProduct.commissionRate}
          description={selectedProduct.description as string}
          openLinkDescription="View on Amazon"
          onClose={handleProductDetailClose}
          onSubmit={handleProductSubmit}
          onPrevious={handleProductDetailClose}
          onClickOpenLink={() => window.open(selectedProduct.url, '_blank')}
        />
      )}
      {showAddProductModal && productToAdd && addModalProduct && (
        <AddAffiliateProduct
          product={addModalProduct}
          onClose={handleAddAffiliateProductClose}
          onDone={result => handleProductDone(productToAdd, result)}
          onPrevious={handleAddAffiliateProductPrevious}
          errors={{ image: imageUploadError, commission: trackingUrlError }}
          key={''}
          loading={modalLoading}
        />
      )}
      <AffiliateHowItWorks
        visible={howItWorksModalVisible}
        onClickPrevious={handleHowItWorksPrevious}
        onSettingsClick={handleSettingsClick}
      />
    </>
  )
}

export const AffiliateModal = selfless(ui, useSelf)
export default AffiliateModal
