import { ApiResponse, ApisauceInstance, create } from 'apisauce'

import { getGeneralApiProblem } from 'services/api/apiProblem'
import { ApiConfig, DEFAULT_RYE_GRAPHQL_API_CONFIG } from './api.config'
import { GetProductsByIdsQuery, ShopifyCollectionQuery } from './rye-gql/types'
import ShopifyServices from '../ShopifyService'

type WithData<T> = {
  data?: T
}

export default class RyeApi {
  api: ApisauceInstance

  private currentAuthToken: string | null = null

  constructor(
    private shopifyApi: ShopifyServices,
    config: ApiConfig = DEFAULT_RYE_GRAPHQL_API_CONFIG
  ) {
    this.api = create({
      baseURL: config.url,
      timeout: config.timeout
    })

    this.api.axiosInstance.interceptors.request.use(
      async config => {
        const headers: Record<string, string> = {
          Authorization: await this.generateAuthHeader()
        }
        config.headers = { ...config.headers, ...headers }
        return config
      },
      error => {
        return Promise.reject(error)
      }
    )
  }

  private async generateAuthHeader() {
    if (!this.currentAuthToken) {
      this.currentAuthToken = await this.shopifyApi.getAuthToken()
    }

    return `Bearer ${this.currentAuthToken}`
  }

  async sendQuery<T = any>(
    query: string,
    variables: Record<string, unknown> = {},
    operationName?: string
  ): Promise<ApiResponse<WithData<T>>> {
    const tryQuery = async () => {
      return await this.api.post<WithData<T>>(
        '',
        JSON.stringify({
          operationName,
          variables,
          query
        })
      )
    }

    try {
      return await tryQuery()
    } catch (e) {
      if ((e as any)?.response?.status === 401) {
        this.currentAuthToken = null
        return await tryQuery()
      }
      throw e
    }
  }

  async getCollections(
    shopifyStoreDomain: string,
    after: string | null
  ): Promise<any> {
    const variable = {
      canonicalDomain: shopifyStoreDomain
    }
    const query = `
      query GetCollections($canonicalDomain: String!) {
        integratedShopifyStore(canonicalDomain: $canonicalDomain) {
          canonicalDomain
          collectionsConnection(first:100, after: ${after}) {
            edges {
              node {
                id
                title
              }
            }
            pageInfo {
              hasNextPage
              endCursor
            }
          }
        }
      }
    `
    const response = await this.sendQuery(query, variable)
    const data = response.data?.data

    if (!response.ok) {
      return getGeneralApiProblem(response as any)
    }
    return {
      ok: response.ok,
      response: data.integratedShopifyStore.collectionsConnection
    }
  }

  async getCollection(collectionId: string) {
    const variable = {
      id: collectionId
    }
    const query = `
      query ShopifyCollection($id: String!) {
        shopifyCollection(id: $id) {
          ${collectionFragment}
        }
      }
    `
    const response = await this.sendQuery<ShopifyCollectionQuery>(
      query,
      variable
    )
    const data = response.data?.data
    if (!response.ok) {
      return {
        ...getGeneralApiProblem(response as any),
        ok: false
      }
    }
    return {
      ok: response.ok,
      response: data
    }
  }

  async getProducts(
    shopifyStoreDomain: string,
    offset = 0,
    pageSize = 100
  ): Promise<any> {
    const variable = {
      canonicalDomain: shopifyStoreDomain
    }
    const query = `
      query GetShopifyProductByDomain($canonicalDomain: String!) {
        productsByDomainV2(
          input: { domain: $canonicalDomain }
          pagination: { limit: ${pageSize}, offset: ${offset} }
        ) {
          ${productFragment}
        }
      }
    `
    const response = await this.sendQuery(query, variable)
    const data = response.data?.data

    if (!response.ok) {
      return getGeneralApiProblem(response as any)
    }
    return {
      ok: response.ok,
      response: data.productsByDomainV2
    }
  }

  async getProductsByIds(shopDomain: string, productIds: string[]) {
    const variables = {
      // TODO Make sure GQL api supports domains too
      storeCanonicalDomain: shopDomain,
      productIDs: productIds
    }
    const query = `
      query GetProductsByIds($storeCanonicalDomain: String!, $productIDs: [ID!]!) {
        integratedShopifyStore(canonicalDomain: $storeCanonicalDomain) {
          productsByIDs(ids: $productIDs) {
            ${productFragment}
          }
        }
      }
    `
    const response = await this.sendQuery<GetProductsByIdsQuery>(
      query,
      variables
    )
    const data = response.data?.data

    if (!response.ok) {
      return {
        ...getGeneralApiProblem(response as any),
        ok: false
      }
    }
    return {
      ok: response.ok,
      response: data?.integratedShopifyStore
    }
  }

  async checkIfAppInstalled(shopifyStoreDomain: string): Promise<any> {
    const variable = {
      canonicalDomain: shopifyStoreDomain
    }
    const query = `
      query GetIntegratedStoreDomain($canonicalDomain: String!) {
        integratedShopifyStore(canonicalDomain: $canonicalDomain) {
          canonicalDomain
        }
      }
    `
    const response = await this.sendQuery(query, variable)
    const data = response.data?.data

    if (!response.ok) {
      return getGeneralApiProblem(response as any)
    }
    return {
      ok: response.ok,
      response: data?.integratedShopifyStore?.canonicalDomain
    }
  }

  async generateRyeShopifyAppInstalLink(storeCanonicalDomain: string) {
    const variables = {
      storeCanonicalDomain
    }
    const query = `
      query shopifyInstallationLink($storeCanonicalDomain: String!) {
        shopifyApp {
          installationLink(storeCanonicalDomain: $storeCanonicalDomain) {
            url
          }
        }
      }
    `

    const response = await this.sendQuery(query, variables)
    const data = response.data?.data

    if (!response.ok) {
      return {
        ...getGeneralApiProblem(response as any),
        ok: false
      }
    }

    return {
      ok: response.ok,
      response: data?.shopifyApp.installationLink.url
    }
  }
}

const collectionFragment = `
  id
  title
  description
  productsConnection {
    edges {
      node {
        id
        title
        variants {
          title
          image {
            url
          }
        }
        images {
          url
        }
        price {
          displayValue
          value
          currency
        }
      }
    }
    pageInfo {
      startCursor
    }
  }
`

const productFragment = `
  id
  title
  marketplace
  description
  isAvailable
  images {
    url
  }
  variants {
    image {
      url
    }
    title
  }
  price {
    value
    currency
    displayValue
  }
`
