import React, { Component } from 'react'
import { withActions, registerComponent } from '@financial-times/x-interaction'
import { normalisePost } from './normalisePost'
import { dispatchEvent } from './dispatchEvent'
import { PostTracker } from './post-tracker'
import { LiveBlogPost } from '../LiveBlogPost'

/* eslint-disable @typescript-eslint/no-explicit-any */
//TODO: CI-1975 remove "any" types
// this has been ported from x-dash, and a lot of what is expected into these arguments will take a lot of digging

// checks to see if there is already a post and if not inserts it. tracks with dispatchEvent
const withLiveBlogWrapperActions = withActions({
  insertPost(newPost: any, wrapper: any) {
    return (props: any) => {
      const normalisedNewPost = normalisePost(newPost)
      const newPostAlreadyExists = props.posts.find(
        (post: any) => post.id === normalisedNewPost.id
      )
      if (!newPostAlreadyExists) {
        props.posts.unshift(normalisedNewPost)
        dispatchEvent(wrapper, 'LiveBlogWrapper.INSERT_POST', {
          post: normalisedNewPost,
        })
      }

      return props
    }
  },
})

type LiveBlogWrapperTypes = {
  articleUrl: string //base url for the article
  posts: Array<any> // a list of liveblog posts //TODO: prob useful extracted into a posts type and use instead of any
  ads?: any
  showShareButtons: boolean // condition to show shareButtons
  showShareLinkButton: boolean // condition to show shareLinkButton
  id: string //id of the liveblog package
  liveBlogWrapperElementRef?: React.RefObject<any> | undefined // react ref, used by FT App
  postTrackerConfig: any //optional config for tracking post //TODO: Body has postTrackerConfig type which could be useful
  serialiser: any //TODO: keeps TS happy, but maybe not required?
}

type LiveBlogWrapperState = { postCount?: number; tracker?: any }

class BaseLiveBlogWrapper extends Component<
  LiveBlogWrapperTypes,
  LiveBlogWrapperState
> {
  state: LiveBlogWrapperState = {}

  componentDidUpdate() {
    if (
      this.state.tracker &&
      this.props.posts.length !== this.state.postCount
    ) {
      this.state.tracker.observeNewElements()
      this.setState({ postCount: this.props.posts.length })
    }
  }

  componentWillUnmount() {
    if (this.state.tracker) {
      this.state.tracker.destroy()
    }
  }

  componentDidMount() {
    if (this.props.posts && this.props.posts.length && !this.state.tracker) {
      this.setUp()
    }
  }

  setUp() {
    const { onEntersViewport, onRead, onError, usePostTracker } =
      this.props.postTrackerConfig || {}

    if (!usePostTracker) {
      return
    }

    if (
      typeof onEntersViewport !== 'function' ||
      typeof onRead !== 'function' ||
      typeof onError !== 'function'
    ) {
      console.error(
        'onEntersViewport, onRead and onError callback functions are required to use Post tracker'
      )
      return
    }

    const tracker = this.setUpPostTracking(
      { onEntersViewport, onRead, onError },
      this.props.id
    )
    this.setState({
      tracker,
    })
  }

  /**
   *
   * @param {Object} callbacks - Callbacks to be called by PostTracker class
   * @param {Function} callbacks.onEntersViewport - function to be called when the criteria for `view` in Post tracker has been fulfiled
   * @param {Function} callbacks.onRead - function to be called when the criteria for `read` in Post tracker has been fulfiled
   * @param {Function} callbacks.onError - function to be called when PostTracker encounters an error
   * @param {string} id
   * @returns {PostTracker}
   */
  setUpPostTracking(callbacks: any, id: string) {
    /**
     * @type {import('./post-tracker').PostTrackerConfig}
     */
    const config = {
      query:
        'article[data-trackable="live-post"],article[data-trackable="pinned-post"]',
      minMillisecondsToReport: 5000,
      returnVisibleElement: true,
      observerUpdateEventString: 'LiveBlogWrapper.INSERT_POST',
      liveBlogWrapperQuery: `div[data-live-blog-wrapper-id="${id}"]`,
      liveBlogWrapper: this.props.liveBlogWrapperElementRef
        ? this.props.liveBlogWrapperElementRef.current
        : undefined,
      onEntersViewport: (event: any) => callbacks.onEntersViewport(event),
      onRead: (event: any) => callbacks.onRead(event),
      onError: (event: any) => callbacks.onError(event),
    }

    /**
     * @type {PostTracker}
     */
    return new PostTracker(config)
  }

  render() {
    const {
      posts = [],
      ads = {},
      articleUrl,
      showShareButtons,
      showShareLinkButton,
      id,
      liveBlogWrapperElementRef,
    } = this.props

    //TODO: CI-1975 these types can move out when we have a posts type properly defined and shared
    posts.sort(
      (
        a: {
          publishedDate: any
          publishedTimestamp: any
          isPinned: boolean
        },
        b: {
          publishedDate: any
          publishedTimestamp: any
          isPinned: boolean
        }
      ) => {
        if (a.isPinned && !b.isPinned) {
          return -1 // A is pinned, B is not, A should come first
        }

        if (b.isPinned && !a.isPinned) {
          return 1 // B is pinned, A is not, B should come first
        }

        // For posts both pinned or both not pinned, sort by date
        const timestampA = a.publishedDate || a.publishedTimestamp
        const timestampB = b.publishedDate || b.publishedTimestamp

        // Newer posts on top
        if (timestampA > timestampB) {
          return -1
        }

        if (timestampB > timestampA) {
          return 1
        }

        return 0
      }
    )

    const postElements = posts.map((post: any, index: number) => (
      <LiveBlogPost
        key={`live-blog-post-${post.id}`}
        {...post}
        articleUrl={articleUrl}
        showShareButtons={showShareButtons}
        showShareLinkButton={showShareLinkButton}
        ad={ads[index]}
      />
    ))

    return (
      <div
        className="x-live-blog-wrapper"
        data-live-blog-wrapper-id={id}
        ref={liveBlogWrapperElementRef}
        data-component="live-blog-wrapper"
      >
        {postElements}
      </div>
    )
  }
}

const LiveBlogWrapper = withLiveBlogWrapperActions(BaseLiveBlogWrapper)
// This enables the component to work with x-interaction hydration
registerComponent(LiveBlogWrapper, 'LiveBlogWrapper')

export { LiveBlogWrapper }
