import { graphql } from 'react-apollo'
import { ApolloClient } from 'apollo-boost'
import * as jwt from 'jsonwebtoken'

import * as userAccessToken from './types/getUserAccessToken'
import * as userPackages from './types/getUserPackages'
import * as getPackage from './types/getPackage'
import * as getScript from './types/getScript'
import * as publish from './types/publish'
import * as refreshAccessTokenType from './types/refreshAccessToken'
import * as subscription from './types/subscription'
import * as userSignUp from './types/userSignUp'
import * as userConfirmSignUp from './types/userConfirmSignUp'
import * as search from './types/search'
import * as getPlatforms from './types/platforms'
import * as getAsset from './types/getAsset'

import getUserAccountTokenQuery from './getUserAccessToken'
import getPlatformsQuery from './getPlatforms'
import searchQuery from './search'
import getUserPackagesQuery from './getUserPackages'
import getPackageQuery from './getPackage'
import getScriptQuery from './getScript'
import publishMutation from './publish'
import refreshAccessTokenMutation from './refreshAccessToken'
import getSubscriptionQuery from './getSubscription'
import userSignUpMutation from './userSignUp'
import userConfirmSignUpMutation from './userConfirmSignUp'
import assetQuery from './getAsset'
import { rsort } from 'semver'
import SearchResults from '../screens/search/SearchResults'

let authSingleton: userAccessToken.getUserAccessToken_userAccessToken | null = null

export function setAuth(
  auth: userAccessToken.getUserAccessToken_userAccessToken
) {
  authSingleton = auth
  localStorage.setItem('auth', JSON.stringify(auth))
}

export function getAuth(): userAccessToken.getUserAccessToken_userAccessToken | null {
  if (authSingleton !== null) {
    return authSingleton
  } else {
    const localAuthJson = localStorage.getItem('auth')
    if (localAuthJson !== null) {
      return JSON.parse(localAuthJson)
    } else {
      return null
    }
  }
}

export function logout() {
  localStorage.clear()
  window.location.replace('/')
}

export function getUsername(): string | null {
  const auth = getAuth()

  if (!auth) {
    return null
  } else {
    const { username } = jwt.decode(auth.accessToken as string) as any
    return username
  }
}

export async function refreshAuth(
  client: ApolloClient<any>
): Promise<userAccessToken.getUserAccessToken_userAccessToken | null> {
  const auth = getAuth()

  if (!auth) {
    return null
  } else {
    const { refreshToken } = auth

    if (!refreshToken) {
      return null
    }

    const res = await refreshAccessToken(client, {
      input: {
        refreshToken,
      },
    })

    if (res && res.data && res.data.userRefreshAccessToken) {
      return res.data.userRefreshAccessToken
    } else {
      return null
    }
  }
}

export async function refreshAccessToken(
  client: ApolloClient<any>,
  variables: refreshAccessTokenType.refreshAccessTokenVariables
) {
  return client.mutate<refreshAccessTokenType.refreshAccessToken>({
    mutation: refreshAccessTokenMutation,
    variables,
  })
}

export async function getSubscription(
  client: ApolloClient<any>,
  variables: subscription.subscriptionVariables
) {
  return client.query<subscription.subscription>({
    query: getSubscriptionQuery,
    variables,
  })
}

export async function getUserAccessToken(
  client: ApolloClient<any>,
  variables: userAccessToken.getUserAccessTokenVariables
) {
  return client.query<userAccessToken.getUserAccessToken>({
    query: getUserAccountTokenQuery,
    variables,
  })
}

export const withUserPackages = graphql<
  userPackages.getUserPackagesVariables,
  userPackages.getUserPackages
>(getUserPackagesQuery, {
  options: () => ({
    variables: {
      input: {
        username: getUsername(),
      },
    },
  }),
})

export const withPackage = graphql<
  getPackage.getPackageVariables,
  getPackage.getPackage
>(getPackageQuery, {
  name: 'package',
  options: (props: any) => ({
    variables: {
      input: {
        name: props.name,
        namespace: props.namespace,
      },
    },
  }),
})

export const withScriptFromRoute = graphql<
  getScript.getScriptVariables,
  getScript.getScript
>(getScriptQuery, {
  options: (props: any) => ({
    variables: {
      input: {
        name: props.match.params.name,
        namespace: props.match.params.namespace || undefined,
        version: props.match.params.version,
      },
    },
  }),
})

export const withPublish = graphql<publish.publishVariables, publish.publish>(
  publishMutation
)

/**
 * REQUIRES withPackage
 */
export const withScript = graphql<
  getScript.getScriptVariables,
  getScript.getScript
>(getScriptQuery, {
  name: 'script',
  skip: (props: any) =>
    !props.package || props.package.loading || !props.package.package,
  options: (props: any) => {
    const pkg: getPackage.getPackage_package = props.package.package

    let [version] = rsort(
      (pkg.scriptMetadata as getPackage.getPackage_package_scriptMetadata[]).map(
        md => md.version
      )
    )

    if (props.version) {
      version = props.version
    }

    return {
      variables: {
        input: {
          name: pkg.name,
          namespace: pkg.namespace || undefined,
          version,
        },
      },
    }
  },
})

export const withPlatforms = graphql<{}, getPlatforms.platforms>(
  getPlatformsQuery,
  {
    name: 'platforms',
  }
)

export const withAsset = graphql<{}, getAsset.getAsset>(assetQuery, {
  name: 'asset',
  skip: (props: any) => !props.id,
  options: (props: any) => ({ variables: { input: { id: props.id } } }),
})

export const withUserSignUp = graphql<
  userSignUp.userSignUpVariables,
  userSignUp.userSignUp
>(userSignUpMutation)

export const withUserConfirmSignUp = graphql<
  userConfirmSignUp.userConfirmSignUpVariables,
  userConfirmSignUp.userConfirmSignUp
>(userConfirmSignUpMutation)

export const withSearchResults = graphql<search.searchVariables, search.search>(
  searchQuery,
  {
    name: 'searchResult',
    options: (props: any) => ({
      variables: {
        input: {
          term: props.term,
          category: props.category,
          platform:
            props.platform.os && props.platform.dist && props.platform.codename
              ? props.platform
              : undefined,
        },
      },
    }),
  }
)
