import * as Pusher from 'pusher-js'
import { get  } from 'lodash'
import { Observable } from 'rxjs'
import { combineEpics, ActionsObservable } from 'redux-observable'
import { Store } from 'redux'
import { AUTHENTICATED } from '@lml/cosmo-ui-auth'
import { RootState } from '../reducers'
import { Pusher as PusherInterface } from 'pusher-js'
import { formatChannelName } from '../utils'
import { PusherMetadataModel } from '../interfaces'
import {
    setMetadata,
    pusherHealthcheckError,
    PUSHER_CONNECT,
    SUBSCRIBE_CHANNEL,
    pusherConnectionSuccess,
    pusherConnectionError,
    PusherConnectAction,
    PusherSubscribeChannelAction,
} from '../actions'

// not exactly functional but lets expose it anyway
// use at your own risk - no guarantees
export let pusherClient: PusherInterface

export const connectToPusher =
    (action$: ActionsObservable<PusherConnectAction>, store: Store<RootState>): Observable<any> =>
        action$.ofType(PUSHER_CONNECT)
            .mergeMap((action: PusherConnectAction) => Observable.create((observer: any) => {
                const options = {
                    cluster: action.cluster,
                    ...action.options,
                }

                pusherClient = new Pusher(action.key, options)

                pusherClient.connection.bind('connected', (result: any) => {
                    observer.next(pusherConnectionSuccess(pusherClient, result))
                });

                pusherClient.connection.bind('error', (error: any) => {
                    observer.next(pusherConnectionError(error))
                });

            }))


/**
 * An epic to subscribe to a channel
 *
 * @param action$
 * @param store
 */
export const subscribeToChannel =
    (action$: ActionsObservable<PusherSubscribeChannelAction>, store: Store<RootState>) =>
        action$.ofType(SUBSCRIBE_CHANNEL)
            .mergeMap((action: PusherSubscribeChannelAction) =>
                Observable.create((observer: any) => {
                    const { events } = action
                    const channel = pusherClient.subscribe(formatChannelName(action.channel))

                    if (Array.isArray(events)) {
                        events.forEach(e => {
                            channel.bind(e, (message: any) => {
                                observer.next(action.callback(message))
                            })
                        })

                    } else {
                        channel.bind(events, (message: any) => {
                            observer.next(action.callback(message))
                        })
                    }
                    // always bind to metadata regardless of channel
                    // the backend team should be providing this as a health check on all channels
                    channel.bind('METADATA', (message: PusherMetadataModel) => {
                        const { items } = message
                        const errors = Object.keys(items)
                            .filter((event: string) => items[event].error > 0)
                        if (errors) {
                            errors.forEach((event: string) => {
                                observer.next(pusherHealthcheckError(action.channel, event, message))
                            })
                        }
                        observer.next(setMetadata(action.channel, message))
                    })
                }))