/* eslint-disable camelcase,@typescript-eslint/no-non-null-assertion */
/* eslint-env browser */
import React, { ComponentProps, useEffect, useReducer } from 'react'
import { parseHostname } from '@ta-interaktiv/parse-hostname'
import { GoogleTagManager } from '@ta-interaktiv/react-gtm'
import { ArticleId } from '@ta-interaktiv/types'
import {
  getTitle,
  isPremium,
  getCategory,
  getSubCategory,
  getPublishingDate,
  getAuthors
} from '@ta-interaktiv/api-access'

export interface Props extends Partial<ArticleId> {
  /** Article ID of the link article in
   * the CD. If not available it will assume that content is being loaded in an
   * iframe. So, yes, you can use this component without adding the
   * `articleId` attribute. Make sure that you also pass an article ID when
   * passing in the statistics configuration and GTM ID directly, otherwise
   * WEMF won't track! */
  articleId?: string

  /** Google Tag Manager ID. If this isn't
   * passed in, the component will fetch the necessary data from the Newsnet
   * API. */
  gtmId: string
}

const domain = parseHostname().publicationName

/**
 * Create a type based on the GTM Component props, that only contains the information
 * that we need to get from the APIs
 */
type ReducedGoogleTagManagerProps = Omit<
  ComponentProps<typeof GoogleTagManager>,
  'gtmId' | 'event' | 'article_id' | 'article_length'
>

/**
 * A list of all the properties we load from the APIs, and whether they have
 * been loaded.
 */
type HasLoadedList = {
  [key in keyof ReducedGoogleTagManagerProps]: boolean
}

type Action = {
  type: 'SET_VALUE'
  payload: string | number
  meta: keyof ReducedGoogleTagManagerProps
}

interface ReducerState {
  hasLoaded: HasLoadedList
  values: ReducedGoogleTagManagerProps
}

const reducer = (state: ReducerState, action: Action) => {
  switch (action.type) {
    case 'SET_VALUE':
      return {
        hasLoaded: {
          ...state.hasLoaded,
          [action.meta]: true
        },
        values: {
          ...state.values,
          [action.meta]: action.payload
        }
      }
  }

  return state
}

/**
 * Create the dispatch object
 * @param value
 * @param key
 */
const setValue: (
  value: string | number,
  key: keyof ReducedGoogleTagManagerProps
) => Action = (value, key) => ({
  type: 'SET_VALUE',
  payload: value,
  meta: key
})

const defaultValues: ReducedGoogleTagManagerProps = {
  article_title: '',
  article_type: 'interactive-standalone',
  pagetype: 'content',
  category: '',
  subcategory: '',
  premium: 0,
  publish_date: '',
  author_name: ''
}

const defaultLoadingValues = Object.keys(defaultValues).reduce<HasLoadedList>(
  (previousValue, currentValue) => {
    previousValue[currentValue as keyof HasLoadedList] = false
    return previousValue
  },
  {} as HasLoadedList
)

/**
 * Component to track visits to a website, accessing the Newsnet API for all
 * tracking information. Currently supports GTM.
 */
export function Tracking({ gtmId, articleId }: Props) {
  const [state, dispatch] = useReducer(reducer, {
    hasLoaded: defaultLoadingValues,
    values: defaultValues
  })

  useEffect(() => {
    if (articleId) {
      try {
        // Title
        getTitle(domain, articleId).then(title =>
          dispatch(setValue(title ?? '', 'article_title'))
        )

        // Article Type
        dispatch(setValue('interactive-standalone', 'article_type'))

        // Page type needs to be set to content when an article ID is available
        dispatch(setValue('content', 'pagetype'))

        // Category
        getCategory(domain, articleId).then(category =>
          dispatch(setValue(category ?? '', 'category'))
        )

        // Sub Category
        getSubCategory(domain, articleId).then(subcategory =>
          dispatch(setValue(subcategory ?? '', 'subcategory'))
        )

        // Premium
        isPremium(domain, articleId).then(isPremium =>
          dispatch(setValue(isPremium ? 1 : 0, 'premium'))
        )

        // Publish Date
        getPublishingDate(domain, articleId).then(date =>
          dispatch(setValue(date ?? '', 'publish_date'))
        )

        // Author name
        getAuthors(domain, articleId).then((authorList: any) =>
          dispatch(setValue(authorList.join(', '), 'author_name'))
        )
      } catch (e) {
        console.debug(
          '[Tracking] Could not set up tracking due to an Error.',
          e
        )
      }
    } else {
      // Title
      dispatch(setValue(window.document.title, 'article_title'))

      // Article Type
      dispatch(setValue('interactive-embeddable', 'article_type'))

      // Page Type
      dispatch(setValue('embeddable', 'pagetype'))

      // Category
      dispatch(setValue('', 'category'))

      // Sub category
      dispatch(setValue('', 'subcategory'))

      // Premium
      dispatch(setValue(0, 'premium'))

      // Publish Date
      dispatch(setValue('', 'publish_date'))

      // Author name
      dispatch(setValue('', 'author_name'))
    }
  }, [articleId])

  if (!gtmId) {
    return null
  }

  // When we have loaded all the properties, render the GTM component
  if (Object.values(state.hasLoaded).every(val => val)) {
    return (
      <GoogleTagManager
        gtmId={gtmId}
        article_id={articleId}
        event='pageview'
        {...state.values}
      />
    )
  }

  return null
}
