/*
 * Copyright OpenSearch Contributors
 * SPDX-License-Identifier: Apache-2.0
 */

package org.opensearch.commons.notifications

import com.nhaarman.mockitokotlin2.whenever
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.Answers
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.junit.jupiter.MockitoExtension
import org.opensearch.action.ActionListener
import org.opensearch.action.ActionType
import org.opensearch.client.node.NodeClient
import org.opensearch.commons.destination.response.LegacyDestinationResponse
import org.opensearch.commons.notifications.action.CreateNotificationConfigRequest
import org.opensearch.commons.notifications.action.CreateNotificationConfigResponse
import org.opensearch.commons.notifications.action.DeleteNotificationConfigRequest
import org.opensearch.commons.notifications.action.DeleteNotificationConfigResponse
import org.opensearch.commons.notifications.action.GetChannelListRequest
import org.opensearch.commons.notifications.action.GetChannelListResponse
import org.opensearch.commons.notifications.action.GetNotificationConfigRequest
import org.opensearch.commons.notifications.action.GetNotificationConfigResponse
import org.opensearch.commons.notifications.action.GetPluginFeaturesRequest
import org.opensearch.commons.notifications.action.GetPluginFeaturesResponse
import org.opensearch.commons.notifications.action.LegacyPublishNotificationRequest
import org.opensearch.commons.notifications.action.LegacyPublishNotificationResponse
import org.opensearch.commons.notifications.action.SendNotificationResponse
import org.opensearch.commons.notifications.action.UpdateNotificationConfigRequest
import org.opensearch.commons.notifications.action.UpdateNotificationConfigResponse
import org.opensearch.commons.notifications.model.Channel
import org.opensearch.commons.notifications.model.ChannelList
import org.opensearch.commons.notifications.model.ChannelMessage
import org.opensearch.commons.notifications.model.ConfigType
import org.opensearch.commons.notifications.model.DeliveryStatus
import org.opensearch.commons.notifications.model.EventSource
import org.opensearch.commons.notifications.model.EventStatus
import org.opensearch.commons.notifications.model.NotificationConfig
import org.opensearch.commons.notifications.model.NotificationConfigInfo
import org.opensearch.commons.notifications.model.NotificationConfigSearchResult
import org.opensearch.commons.notifications.model.NotificationEvent
import org.opensearch.commons.notifications.model.SeverityType
import org.opensearch.commons.notifications.model.Slack
import org.opensearch.core.rest.RestStatus
import java.time.Instant

@Suppress("UNCHECKED_CAST")
@ExtendWith(MockitoExtension::class)
internal class NotificationsPluginInterfaceTests {

    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    private lateinit var client: NodeClient

    @Test
    fun createNotificationConfig() {
        val request = mock(CreateNotificationConfigRequest::class.java)
        val response = CreateNotificationConfigResponse("configId")
        val listener: ActionListener<CreateNotificationConfigResponse> =
            mock(ActionListener::class.java) as ActionListener<CreateNotificationConfigResponse>

        doAnswer {
            (it.getArgument(2) as ActionListener<CreateNotificationConfigResponse>)
                .onResponse(response)
        }.whenever(client).execute(any(ActionType::class.java), any(), any())

        NotificationsPluginInterface.createNotificationConfig(client, request, listener)
        verify(listener, times(1)).onResponse(eq(response))
    }

    @Test
    fun updateNotificationConfig() {
        val request = mock(UpdateNotificationConfigRequest::class.java)
        val response = UpdateNotificationConfigResponse("configId")
        val listener: ActionListener<UpdateNotificationConfigResponse> =
            mock(ActionListener::class.java) as ActionListener<UpdateNotificationConfigResponse>

        doAnswer {
            (it.getArgument(2) as ActionListener<UpdateNotificationConfigResponse>)
                .onResponse(response)
        }.whenever(client).execute(any(ActionType::class.java), any(), any())

        NotificationsPluginInterface.updateNotificationConfig(client, request, listener)
        verify(listener, times(1)).onResponse(eq(response))
    }

    @Test
    fun deleteNotificationConfig() {
        val request = mock(DeleteNotificationConfigRequest::class.java)
        val response = DeleteNotificationConfigResponse(mapOf(Pair("sample_config_id", RestStatus.OK)))
        val listener: ActionListener<DeleteNotificationConfigResponse> =
            mock(ActionListener::class.java) as ActionListener<DeleteNotificationConfigResponse>

        doAnswer {
            (it.getArgument(2) as ActionListener<DeleteNotificationConfigResponse>)
                .onResponse(response)
        }.whenever(client).execute(any(ActionType::class.java), any(), any())

        NotificationsPluginInterface.deleteNotificationConfig(client, request, listener)
        verify(listener, times(1)).onResponse(eq(response))
    }

    @Test
    fun getNotificationConfig() {
        val request = mock(GetNotificationConfigRequest::class.java)
        val response = mockGetNotificationConfigResponse()
        val listener: ActionListener<GetNotificationConfigResponse> =
            mock(ActionListener::class.java) as ActionListener<GetNotificationConfigResponse>

        doAnswer {
            (it.getArgument(2) as ActionListener<GetNotificationConfigResponse>)
                .onResponse(response)
        }.whenever(client).execute(any(ActionType::class.java), any(), any())

        NotificationsPluginInterface.getNotificationConfig(client, request, listener)
        verify(listener, times(1)).onResponse(eq(response))
    }

    @Test
    fun getPluginFeatures() {
        val request = mock(GetPluginFeaturesRequest::class.java)
        val response = GetPluginFeaturesResponse(
            listOf("config_type_1", "config_type_2", "config_type_3"),
            mapOf(
                Pair("FeatureKey1", "FeatureValue1"),
                Pair("FeatureKey2", "FeatureValue2"),
                Pair("FeatureKey3", "FeatureValue3")
            )
        )
        val listener: ActionListener<GetPluginFeaturesResponse> =
            mock(ActionListener::class.java) as ActionListener<GetPluginFeaturesResponse>

        doAnswer {
            (it.getArgument(2) as ActionListener<GetPluginFeaturesResponse>)
                .onResponse(response)
        }.whenever(client).execute(any(ActionType::class.java), any(), any())

        NotificationsPluginInterface.getPluginFeatures(client, request, listener)
        verify(listener, times(1)).onResponse(eq(response))
    }

    @Test
    fun getChannelList() {
        val sampleConfig = Channel(
            "config_id",
            "name",
            "description",
            ConfigType.SLACK
        )

        val request = mock(GetChannelListRequest::class.java)
        val response = GetChannelListResponse(ChannelList(sampleConfig))
        val listener: ActionListener<GetChannelListResponse> =
            mock(ActionListener::class.java) as ActionListener<GetChannelListResponse>

        doAnswer {
            (it.getArgument(2) as ActionListener<GetChannelListResponse>)
                .onResponse(response)
        }.whenever(client).execute(any(ActionType::class.java), any(), any())

        NotificationsPluginInterface.getChannelList(client, request, listener)
        verify(listener, times(1)).onResponse(eq(response))
    }

    @Test
    fun sendNotification() {
        val notificationInfo = EventSource(
            "title",
            "reference_id",
            SeverityType.HIGH,
            listOf("tag1", "tag2")
        )
        val channelMessage = ChannelMessage(
            "text_description",
            "<b>htmlDescription</b>",
            null
        )

        val sampleStatus = EventStatus(
            "config_id",
            "name",
            ConfigType.SLACK,
            deliveryStatus = DeliveryStatus("404", "invalid recipient")
        )

        val sampleEvent = NotificationEvent(notificationInfo, listOf(sampleStatus))

        val response = SendNotificationResponse(sampleEvent)
        val listener: ActionListener<SendNotificationResponse> =
            mock(ActionListener::class.java) as ActionListener<SendNotificationResponse>

        doAnswer {
            (it.getArgument(2) as ActionListener<SendNotificationResponse>)
                .onResponse(response)
        }.whenever(client).execute(any(ActionType::class.java), any(), any())

        NotificationsPluginInterface.sendNotification(
            client, notificationInfo, channelMessage, listOf("channelId1", "channelId2"), listener
        )
        verify(listener, times(1)).onResponse(eq(response))
    }

    @Test
    fun publishLegacyNotification() {
        val request = mock(LegacyPublishNotificationRequest::class.java)
        val res = LegacyPublishNotificationResponse(LegacyDestinationResponse.Builder().withStatusCode(200).withResponseContent("Nice!").build())
        val l: ActionListener<LegacyPublishNotificationResponse> =
            mock(ActionListener::class.java) as ActionListener<LegacyPublishNotificationResponse>

        doAnswer {
            (it.getArgument(2) as ActionListener<LegacyPublishNotificationResponse>)
                .onResponse(res)
        }.whenever(client).execute(any(ActionType::class.java), any(), any())

        NotificationsPluginInterface.publishLegacyNotification(client, request, l)
        verify(l, times(1)).onResponse(eq(res))
    }

    private fun mockGetNotificationConfigResponse(): GetNotificationConfigResponse {
        val sampleSlack = Slack("https://domain.com/sample_url#1234567890")
        val sampleConfig = NotificationConfig(
            "name",
            "description",
            ConfigType.SLACK,
            configData = sampleSlack
        )
        val configInfo = NotificationConfigInfo(
            "config_id",
            Instant.now(),
            Instant.now(),
            sampleConfig
        )
        return GetNotificationConfigResponse(NotificationConfigSearchResult(configInfo))
    }
}