import {CTAType, INudgesRepository, Nudge} from './repositories/nudges/typings'
import { EventEmitter } from 'events';
import { NotificationAction, NotificationDispatcher } from '../drivers/notifications/typings'
import { DateTime } from 'luxon'
import { INudgeManager, NudgeManagerAction } from './CfNudgeManager.typings';
import {ExceptionObject} from "./typings";
import {toISOLocal} from "../utils";
import CfCore from "./CfCore";
import CfExceptionHandler from "./CfExceptionHandler";
import Navigation from "../modules/Navigation/index";
import {NudgeAction} from "../modules/Navigation/typings";
import {NudgeScreenType} from "./commonTypes";

interface IndexedNudge {
    [key: string]: Nudge
}

export default class NudgeManager extends EventEmitter implements INudgeManager {
    private timersId = []
    private notificationDispatcher
    private nudgesRepository
    private refreshInterval = 5 * 60 * 1000
    private isOneTimeOnly = false
    private nudgeScreenType = NudgeScreenType.None
    private indexedNudges: IndexedNudge = {}

    public constructor(
        userId: string,
        notificationDispatcher: NotificationDispatcher,
        nudgesRepository: INudgesRepository,
        nudgeScreenType = NudgeScreenType.None,
        isOneTimeOnly = false,
        refreshInterval = 5 * 60 * 1000
    ) {
        super()
        this.notificationDispatcher = notificationDispatcher
        this.nudgesRepository = nudgesRepository
        this.refreshInterval = refreshInterval
        this.nudgeScreenType = nudgeScreenType
        this.isOneTimeOnly = isOneTimeOnly
        /*
           - nudges are retrieved periodically, each 5min.
           - in the future this timeout will be configurable
           - dispatched_at will be:
               - current time less 1 hour the first time
               - time of last shown nudge, if exists, or current time next times
        */

        this.notificationDispatcher.on(NotificationAction.Open, (id) => {
            this.emit(NudgeManagerAction.Open, this.indexedNudges[id].ref, this.indexedNudges[id].time)

            if (!this.indexedNudges[id]) {
                return
            }

            const hasActions = () => {
                return (this.indexedNudges[id].nd.cta !== CTAType.None &&
                    this.indexedNudges[id].extra && this.indexedNudges[id].extra.item_pair &&
                    this.indexedNudges[id].extra.item_pair.ids.length == 2 &&
                    this.indexedNudges[id].nd.message.tmpl_cfg &&
                    this.indexedNudges[id].nd.message.tmpl_cfg.item_pair_cfg &&
                    this.indexedNudges[id].nd.message.tmpl_cfg.tmpl_type === "item_pair" &&
                    this.indexedNudges[id].extra.item_pair.ids.length === 2)
            }

            if (!hasActions()) {
                return
            }
            const ctaObject = {
                type: this.indexedNudges[id].nd.cta.toLowerCase(),
                cta_resource: {
                    type:  this.indexedNudges[id].nd.message.tmpl_cfg.item_pair_cfg.item_type,
                    id: this.indexedNudges[id].extra.item_pair.ids[0]
                }
            }

            this.emit(
                NudgeManagerAction.Action,
                ctaObject)

        })

        this.notificationDispatcher.on(NotificationAction.Discard, id => {
            this.emit(NudgeManagerAction.Discard, this.indexedNudges[id].ref, this.indexedNudges[id].time)
        })

        this.notificationDispatcher.on(NotificationAction.Block, id => {
            this.emit(NudgeManagerAction.Block, this.indexedNudges[id].ref, this.indexedNudges[id].time)
        })

        this.notificationDispatcher.on(NotificationAction.Shown, id => {
            this.emit(NudgeManagerAction.Shown, this.indexedNudges[id].ref, this.indexedNudges[id].time)
        })


        const oneHourAgo = new Date()
        oneHourAgo.setHours(oneHourAgo.getHours() - 1)

        // get nudge item:
        this.fetchNudges(userId, this.nudgeScreenType)

        if(!this.isOneTimeOnly){
            setInterval(() => {
                this.fetchNudges(userId, this.nudgeScreenType)
            }, this.refreshInterval)
        }
    }

    private async fetchNudges(userId: string, nudgeScreenType : NudgeScreenType) {
        let nudges;

        try {
            nudges = await this.nudgesRepository.getNudges(userId, nudgeScreenType)
        } catch (e) {
            console.error('Error fetching nudges: ', e)
            return
        }
        nudges
            // .filter(({ ref }) => {
            //     const alreadyShown = this.nudgesRepository.haveBeenAlreadyShown(ref)
            //     return !alreadyShown
            // })
            .forEach((nudge, index) => {
                nudge.internalId = index
                this.indexedNudges[index] = nudge
                this.processNudge(nudge)
            })
    }

    private processNudge(nudge: Nudge) {

        if(!nudge.expire_at){
            nudge.expire_at = "0001-01-01T00:00:00Z"
        }
        const expirationNotDefined = nudge.expire_at === "0001-01-01T00:00:00Z"
        const notExpired = nudge.expire_at > new Date().toISOString()

        if (nudge.nd.message.tmpl_cfg.tmpl_type !== "item_pair" &&
            nudge.nd.message.tmpl_cfg.tmpl_type !== "traits" &&
            nudge.nd.message.tmpl_cfg.tmpl_type !== "") {
            new CfExceptionHandler().throwNudgeException("Invalid template type provided", nudge)
            return
        }

        if (notExpired || expirationNotDefined) {
            this.notificationDispatcher.show(nudge, nudge.internalId)
        }else{
            Navigation.logPushNotificationEvent({
                ref: nudge.ref,
                response: NudgeAction.Expired,
                time: nudge.time
            })
        }
    }

}
