/**
 * @jest-environment jsdom
 */
import NudgeManager from "./CfNudgeManager";

import { DateTime } from 'luxon'
import { CTAType, INudgesRepository } from "./repositories/nudges/typings";
import NudgeRepository from "./repositories/nudges/NudgeRepository";
import { NudgeManagerAction } from "./CfNudgeManager.typings";


describe('BsNudgeManager', () => {

    afterEach(() => {
        jest.clearAllMocks()
    })

    it('Should do not do something when retrieving an empty list of nudges', (done) => {
        // const mockBsNotification = {
        //     show() { return false },
        //     on() { }
        // }

        const mockNudgeRepository = {
            getNudges: jest.fn().mockResolvedValue([]),
            haveBeenAlreadyShown: jest.fn().mockReturnValue(false),
            markNudgeAsShown: jest.fn()
        }

        // const notificationSpy = jest.spyOn(mockBsNotification, 'show')

        // const nudgeManager = new NudgeManager(
        //     'userId',
        //     mockBsNotification,
        //     mockNudgeRepository as INudgesRepository,
        // )

        // setTimeout(() => {
        //     expect(notificationSpy).not.toHaveBeenCalled()

            done()
        }, 1000)
    })

    it('Should show the notification for the given nudge', (done) => {
        const nudgeDate = DateTime.local().plus(20).toJSDate().toISOString()
        const nudgeExpiryDate = DateTime.local().plus({ seconds: 45 }).toJSDate().toISOString()
        const mockBsNotification = {
            show: jest.fn(),
            on: jest.fn()
        }

        const notificationSpy = jest.spyOn(mockBsNotification, 'show')

        const nudgeTitle = 'nudge-title'
        const nudgeBody = 'nudge-body'
        const nudgeId = 15501

        const mockNudgeRepository = {
            getNudges: jest.fn().mockResolvedValue([{
                id: nudgeId,
                dispatched_at: nudgeDate,
                expire_at: nudgeExpiryDate,
                action: {
                    definition: {
                        title: nudgeTitle,
                        body: nudgeBody
                    }
                }
            }]),
            haveBeenAlreadyShown: jest.fn().mockReturnValue(false),
            markNudgeAsShown: jest.fn()
        }

        const nudgeManager = new NudgeManager('userId', mockBsNotification, mockNudgeRepository as INudgesRepository)

        setTimeout(() => {
            // this timeout waits for the setTimeout inside the BsNudgeManager, which
            // should trigger in one second
            expect(notificationSpy).toBeCalledWith(nudgeTitle, nudgeBody, nudgeId)
            done()
        }, 100)
    })

    it('Should show the notification for the given nudge even if its date has already passed, but not expired', (done) => {
        const nudgeDate = DateTime.local().plus({ seconds: -1 }).toJSDate().toISOString()
        const nudgeExpiryDate = DateTime.local().plus({ seconds: 45 }).toJSDate().toISOString()
        const mockBsNotification = {
            show: jest.fn(),
            on: jest.fn()
        }

        const notificationSpy = jest.spyOn(mockBsNotification, 'show')

        const nudgeTitle = 'nudge-title'
        const nudgeBody = 'nudge-body'
        const nudgeId = 15501

        const mockNudgeRepository = {
            getNudges: jest.fn().mockResolvedValue([{
                id: nudgeId,
                dispatched_at: nudgeDate,
                expire_at: nudgeExpiryDate,
                action: {
                    definition: {
                        title: nudgeTitle,
                        body: nudgeBody
                    }
                }
            }]),
            haveBeenAlreadyShown: jest.fn().mockReturnValue(false),
            markNudgeAsShown: jest.fn()
        }

        const nudgeManager = new NudgeManager('userId', mockBsNotification, mockNudgeRepository as INudgesRepository)

        setTimeout(() => {
            // this timeout waits for the setTimeout inside the BsNudgeManager, which
            // should trigger in one second
            expect(notificationSpy).toBeCalledWith(nudgeTitle, nudgeBody, nudgeId)
            done()
        }, 20)
    })

    it('Should discard expired nudges', (done) => {
        const nudgeDate = DateTime.local().plus({ seconds: 20 }).toJSDate().toISOString()
        const nudgeExpiryDate = DateTime.local().plus({ seconds: 3 }).toJSDate().toISOString()

        const mockBsNotification = {
            show: jest.fn(),
            on: jest.fn()
        }

        const notificationSpy = jest.spyOn(mockBsNotification, 'show')

        const nudgeTitle = 'nudge-title'
        const nudgeBody = 'nudge-body'
        const nudgeId = 15501

        const mockNudgeRepository = {
            getNudges: jest.fn().mockResolvedValue([{
                id: nudgeId,
                dispatched_at: nudgeDate,
                expire_at: nudgeExpiryDate,
                action: {
                    definition: {
                        title: nudgeTitle,
                        body: nudgeBody
                    }
                }
            }]),
            haveBeenAlreadyShown: jest.fn().mockReturnValue(false),
            markNudgeAsShown: jest.fn()
        }

        const nudgeManager = new NudgeManager('userId', mockBsNotification, mockNudgeRepository as INudgesRepository)

        setTimeout(() => {
            // this timeout waits for the setTimeout inside the BsNudgeManager, which
            // should trigger in one second
            expect(notificationSpy).not.toBeCalled()
            done()
        }, 200)
    })

    it('Should discard nudges with the same ID (show only a notification regardless many fetches with repeated nudges)', (done) => {
        const nudgeDate = DateTime.local().plus({ seconds: -1 }).toJSDate().toISOString()
        const nudgeExpiryDate = DateTime.local().plus({ seconds: 45 }).toJSDate().toISOString()
        const mockBsNotification = {
            show: jest.fn(),
            on: jest.fn()
        }

        let saveValue = null
        const mockStorage = {
            save(key, value) {
                saveValue = value
            },
            get() {
                return saveValue
            }
        }

        const notificationSpy = jest.spyOn(mockBsNotification, 'show')

        const nudgeTitle = 'nudge-title'
        const nudgeBody = 'nudge-body'
        const nudgeId = 15501

        const mockBsNetwork = {
            get: jest.fn(() => new Promise(r => r(
                [
                    {
                        "id": nudgeId,
                        "action":
                        {
                            "id": nudgeId,
                            "org_proj": "demo_demo",
                            "name": "See what others are up to in {{app}}! ",
                            "description": "",
                            "tags": ["Intrigue"],
                            "type": "push_notification",
                            "target_cohort_ids": null,
                            "definition": {
                                "body": nudgeBody,
                                "language": "en",
                                "linkTo": "",
                                "saveToLibrary": [""],
                                "tags": ["Intrigue"],
                                "title": nudgeTitle
                            },
                            "created_by": "causalfoundry",
                            "created_at": "2022-01-26T09:04:56.383983Z"
                        },
                        "expire_at": nudgeExpiryDate,
                        "dispatched_at": nudgeDate
                    }
                ]))),
            send: jest.fn()
        }

        const nudgesRepository = new NudgeRepository('fake-url', mockBsNetwork, mockStorage)

        const nudgeManager = new NudgeManager('userId', mockBsNotification, nudgesRepository, 20)

        setTimeout(() => {
            // this timeout waits for the setTimeout inside the BsNudgeManager, which
            // should trigger in one second
            expect(notificationSpy).toBeCalledTimes(1)
            done()
        }, 100)
    })

    it('Should discard nudges with the same ID (show only a notification regardless many fetches with repeated nudges) - new nudges in second request', (done) => {
        const nudgeDate = DateTime.local().plus({ seconds: -1 }).toJSDate().toISOString()
        const nudgeExpiryDate = DateTime.local().plus({ seconds: 45 }).toJSDate().toISOString()
        const mockBsNotification = {
            show: jest.fn(),
            on: jest.fn()
        }

        let saveValue = null
        const mockStorage = {
            save(key, value) {
                saveValue = value
            },
            get() {
                return saveValue
            }
        }

        const notificationSpy = jest.spyOn(mockBsNotification, 'show')

        const nudgeTitle = 'nudge-title'
        const nudgeBody = 'nudge-body'
        const nudgeId_1 = 15501
        const nudgeId_2 = 15502

        const mockBsNetwork = {
            get: jest.fn()
                .mockReturnValueOnce(new Promise(r => r(
                    [
                        {
                            "id": nudgeId_1,
                            "action":
                            {
                                "id": nudgeId_1,
                                "org_proj": "demo_demo",
                                "name": "See what others are up to in {{app}}! ",
                                "description": "",
                                "tags": ["Intrigue"],
                                "type": "push_notification",
                                "target_cohort_ids": null,
                                "definition": {
                                    "body": nudgeBody,
                                    "language": "en",
                                    "linkTo": "",
                                    "saveToLibrary": [""],
                                    "tags": ["Intrigue"],
                                    "title": nudgeTitle
                                },
                                "created_by": "causalfoundry",
                                "created_at": "2022-01-26T09:04:56.383983Z"
                            },
                            "expire_at": nudgeExpiryDate,
                            "dispatched_at": nudgeDate
                        }
                    ])))
                .mockReturnValue(new Promise(r => r(
                    [{
                        "id": nudgeId_1,
                        "action":
                        {
                            "id": nudgeId_1,
                            "org_proj": "demo_demo",
                            "name": "See what others are up to in {{app}}! ",
                            "description": "",
                            "tags": ["Intrigue"],
                            "type": "push_notification",
                            "target_cohort_ids": null,
                            "definition": {
                                "body": nudgeBody,
                                "language": "en",
                                "linkTo": "",
                                "saveToLibrary": [""],
                                "tags": ["Intrigue"],
                                "title": nudgeTitle
                            },
                            "created_by": "causalfoundry",
                            "created_at": "2022-01-26T09:04:56.383983Z"
                        },
                        "expire_at": nudgeExpiryDate,
                        "dispatched_at": nudgeDate
                    },
                    {
                        "id": nudgeId_2,
                        "action":
                        {
                            "id": nudgeId_2,
                            "org_proj": "demo_demo",
                            "name": "See what others are up to in {{app}}! ",
                            "description": "",
                            "tags": ["Intrigue"],
                            "type": "push_notification",
                            "target_cohort_ids": null,
                            "definition": {
                                "body": nudgeBody,
                                "language": "en",
                                "linkTo": "",
                                "saveToLibrary": [""],
                                "tags": ["Intrigue"],
                                "title": nudgeTitle
                            },
                            "created_by": "causalfoundry",
                            "created_at": "2022-01-26T09:04:56.383983Z"
                        },
                        "expire_at": nudgeExpiryDate,
                        "dispatched_at": nudgeDate
                    }
                    ]))),
            send: jest.fn()
        }

        const nudgesRepository = new NudgeRepository('fake-url', mockBsNetwork, mockStorage)

        const nudgeManager = new NudgeManager('userId', mockBsNotification, nudgesRepository, 20)

        setTimeout(() => {
            // this timeout waits for the setTimeout inside the BsNudgeManager, which
            // should trigger in one second
            expect(notificationSpy).toBeCalledTimes(2)
            done()
        }, 60)
    })


    it('Should emit an event on call_to_action to open itemId ', async () => {
        const mockBsNotification = {
            show: jest.fn(),
            on: (eventName, clb) => {
                if (eventName !== 'open') {
                    return
                }

                setTimeout(() => {
                    clb(111)
                }, 100)

            }
        }

        const fetchedNudgeData = [{
            id: 111,
            dispatched_at: DateTime.local().plus(10).toJSDate().toISOString(),
            expire_at: DateTime.local().plus(150).toJSDate().toISOString(),
            action: {
                call_to_action: {
                    type: CTAType.Redirect,
                    cta_resource: {
                        type: '',
                        id: '45'
                    }
                },
                definition: {
                    title: 'title-1',
                    body: 'body-1'
                }
            }

        }]

        const mockNudgeRepository = {
            getNudges: jest.fn()
                .mockResolvedValue(fetchedNudgeData),
            haveBeenAlreadyShown: jest.fn().mockReturnValue(false),
            markNudgeAsShown: jest.fn()
        }

        const nudgeManager = new NudgeManager('userId', mockBsNotification, mockNudgeRepository as INudgesRepository, 200)

        const actionCallback = jest.fn()

        nudgeManager.on(NudgeManagerAction.Action, actionCallback)

        await new Promise((r) => setTimeout(r, 300));

        expect(actionCallback).toBeCalled()
    })


    it('Should NOT emit an event on call_to_action if the action is not redirect ', async () => {
        const mockBsNotification = {
            show: jest.fn(),
            on: (eventName, clb) => {
                if (eventName !== 'open') {
                    return
                }

                setTimeout(() => {
                    clb(111)
                }, 100)

                console.log('called method on: ', eventName)
            }
        }

        const fetchedNudgeData = [{
            id: 111,
            dispatched_at: DateTime.local().plus(10).toJSDate().toISOString(),
            expire_at: DateTime.local().plus(150).toJSDate().toISOString(),
            action: {
                call_to_action: {
                    type: CTAType.None,
                    cta_resource: {
                        type: '',
                        id: '45'
                    }
                },
                definition: {
                    title: 'title-1',
                    body: 'body-1'
                }
            }
        }]


        const mockNudgeRepository = {
            getNudges: jest.fn()
                .mockResolvedValue(fetchedNudgeData),
            haveBeenAlreadyShown: jest.fn().mockReturnValue(false),
            markNudgeAsShown: jest.fn()
        }

        const nudgeManager = new NudgeManager('userId', mockBsNotification, mockNudgeRepository as INudgesRepository, 200)

        const actionCallback = jest.fn()

        nudgeManager.on(NudgeManagerAction.Action, actionCallback)

        await new Promise((r) => setTimeout(r, 300));

        expect(actionCallback).not.toBeCalled()
    })

    it('Should NOT emit an event on call_to_action if the id is missing ', async () => {
        const mockBsNotification = {
            show: jest.fn(),
            on: (eventName, clb) => {
                if (eventName !== 'open') {
                    return
                }

                setTimeout(() => {
                    clb(111)
                }, 100)

                console.log('called method on: ', eventName)
            }
        }

        const fetchedNudgeData = [{
            id: 111,
            dispatched_at: DateTime.local().plus(10).toJSDate().toISOString(),
            expire_at: DateTime.local().plus(150).toJSDate().toISOString(),
            action: {
                call_to_action: {
                    type: CTAType.Redirect,
                    cta_resource: {
                        type: '',
                        id: ''
                    }
                },
                definition: {
                    title: 'title-1',
                    body: 'body-1'
                }
            }
        }]


        const mockNudgeRepository = {
            getNudges: jest.fn()
                .mockResolvedValue(fetchedNudgeData),
            haveBeenAlreadyShown: jest.fn().mockReturnValue(false),
            markNudgeAsShown: jest.fn()
        }

        const nudgeManager = new NudgeManager('userId', mockBsNotification, mockNudgeRepository as INudgesRepository, 200)

        const actionCallback = jest.fn()

        nudgeManager.on(NudgeManagerAction.Action, actionCallback)

        await new Promise((r) => setTimeout(r, 300));

        expect(actionCallback).not.toBeCalled()

    })

    it.skip('Should show nudges from differents fetchs', async () => {
        const mockBsNotification = {
            show: jest.fn(),
            on: jest.fn()
        }

        const notificationSpy = jest.spyOn(mockBsNotification, 'show')

        const firstFetch = [{
            id: 111,
            dispatched_at: DateTime.local().plus(10).toJSDate().toISOString(),
            expire_at: DateTime.local().plus(150).toJSDate().toISOString(),
            title: 'title-1',
            body: 'body-1'
        }]

        const secondFetch = [{
            id: 222,
            dispatched_at: DateTime.local().plus(20).toJSDate().toISOString(),
            title: 'title-2',
            body: 'body-2'
        }]

        const mockNudgeRepository = {
            getNudges: jest.fn()
                .mockResolvedValueOnce(firstFetch)
                .mockResolvedValue(secondFetch),
            haveBeenAlreadyShown: jest.fn().mockReturnValue(false),
            markNudgeAsShown: jest.fn()
        }

        const nudgeManager = new NudgeManager(
            'userId',
            mockBsNotification,
            mockNudgeRepository as INudgesRepository,
            200
        )

        await new Promise((r) => setTimeout(r, 300));

        // expect(notificationSpy.mock.calls[0][0]).toEqual(firstFetch[0].title)
        // expect(notificationSpy.mock.calls[1][0]).toEqual(secondFetch[0].title)


})



function BsNudgeEvents(BsNudgeEvents: any) {
    throw new Error("Function not implemented.");
}
/*
// REAL NUDGE RESPONSE
        const response = [
            {
                "id": nudgeId,
                "action":
                {
                    "id": 121,
                    "org_proj": "demo_demo",
                    "name": "See what others are up to in {{app}}! ",
                    "description": "",
                    "tags": ["Intrigue"],
                    "type": "push_notification",
                    "target_cohort_ids": null,
                    "definition": {
                        "body": nudgeBody,
                        "language": "en",
                        "linkTo": "",
                        "saveToLibrary": [""],
                        "tags": ["Intrigue"],
                        "title": nudgeTitle
                    },
                    "created_by": "causalfoundry",
                    "created_at": "2022-01-26T09:04:56.383983Z"
                },
                "dispatched_at": nudgeDate
            }
        ]
*/