package novuhandler

import (
	"strconv"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"

	"github.com/pingcap/dbaas/billing2/utils/log"
	"go.uber.org/zap"

	"github.com/pingcap/dbaas/billing2/forms"
)

type Notification struct {
	Id             string   `json:"_id"`
	TemplateId     string   `json:"_templateId"`
	EnvironmentId  string   `json:"_environmentId"`
	OrganizationId string   `json:"_organizationId"`
	SubscriberId   string   `json:"_subscriberId"`
	TransactionId  string   `json:"transactionId"`
	Channels       []string `json:"channels"`
	To             struct {
		SubscriberId string `json:"subscriberId"`
		Email        string `json:"email,omitempty"`
	} `json:"to"`
	Payload   map[string]interface{} `json:"payload"`
	ExpireAt  time.Time              `json:"expireAt"`
	CreatedAt time.Time              `json:"createdAt"`
	UpdatedAt time.Time              `json:"updatedAt"`
	V         int                    `json:"__v"`
	Template  struct {
		Id       string `json:"_id"`
		Name     string `json:"name"`
		Triggers []struct {
			Type       string `json:"type"`
			Identifier string `json:"identifier"`
			Variables  []struct {
				Name string `json:"name"`
				Type string `json:"type"`
				Id   string `json:"_id"`
				Id1  string `json:"id"`
			} `json:"variables"`
			ReservedVariables   []interface{} `json:"reservedVariables"`
			SubscriberVariables []interface{} `json:"subscriberVariables"`
			Id                  string        `json:"_id"`
			Id1                 string        `json:"id"`
		} `json:"triggers"`
		Id1 string `json:"id"`
	} `json:"template"`
	Subscriber struct {
		Id           string `json:"_id"`
		SubscriberId string `json:"subscriberId"`
		Id1          string `json:"id"`
		Email        string `json:"email,omitempty"`
	} `json:"subscriber"`
	Jobs []struct {
		Digest struct {
			Timed struct {
				WeekDays  []interface{} `json:"weekDays"`
				MonthDays []interface{} `json:"monthDays"`
			} `json:"timed"`
			Events []interface{} `json:"events"`
			Type   string        `json:"type,omitempty"`
			Amount int           `json:"amount,omitempty"`
			Unit   string        `json:"unit,omitempty"`
		} `json:"digest"`
		Id      string `json:"_id"`
		Status  string `json:"status"`
		Payload struct {
			FriendName       string    `json:"friendName"`
			SendAT           time.Time `json:"sendAT,omitempty"`
			Source           string    `json:"__source"`
			CompanyName      string    `json:"companyName,omitempty"`
			FriendRequestUrl string    `json:"friendRequestUrl,omitempty"`
		} `json:"payload"`
		Step struct {
			Metadata struct {
				Timed struct {
					WeekDays  []interface{} `json:"weekDays"`
					MonthDays []interface{} `json:"monthDays"`
				} `json:"timed"`
				DelayPath string `json:"delayPath,omitempty"`
				Type      string `json:"type,omitempty"`
				Amount    int    `json:"amount,omitempty"`
				Unit      string `json:"unit,omitempty"`
			} `json:"metadata"`
			Active           bool   `json:"active"`
			ShouldStopOnFail bool   `json:"shouldStopOnFail"`
			Uuid             string `json:"uuid"`
			Name             string `json:"name"`
			Type             string `json:"type,omitempty"`
			Filters          []struct {
				Children []struct {
					Operator string `json:"operator"`
					On       string `json:"on"`
					Step     string `json:"step"`
					StepType string `json:"stepType"`
					Id       string `json:"_id"`
					Id1      string `json:"id"`
				} `json:"children"`
				Id    string `json:"_id"`
				Id1   string `json:"id"`
				Value string `json:"value,omitempty"`
			} `json:"filters"`
			TemplateId string        `json:"_templateId"`
			ParentId   *string       `json:"_parentId"`
			Id         string        `json:"_id"`
			Variants   []interface{} `json:"variants"`
			Id1        string        `json:"id"`
			Template   struct {
				Id        string `json:"_id"`
				Type      string `json:"type"`
				Active    bool   `json:"active"`
				Subject   string `json:"subject"`
				Variables []struct {
					Name     string `json:"name"`
					Type     string `json:"type"`
					Required bool   `json:"required"`
					Id       string `json:"_id"`
					Id1      string `json:"id"`
				} `json:"variables"`
				Content        string      `json:"content"`
				ContentType    string      `json:"contentType"`
				EnvironmentId  string      `json:"_environmentId"`
				OrganizationId string      `json:"_organizationId"`
				CreatorId      string      `json:"_creatorId"`
				FeedId         interface{} `json:"_feedId"`
				LayoutId       interface{} `json:"_layoutId"`
				Deleted        bool        `json:"deleted"`
				CreatedAt      time.Time   `json:"createdAt"`
				UpdatedAt      time.Time   `json:"updatedAt"`
				V              int         `json:"__v"`
				Id1            string      `json:"id"`
				Cta            struct {
					Data struct {
						Url string `json:"url"`
					} `json:"data"`
					Type   string `json:"type"`
					Action struct {
						Buttons []struct {
							Type    string `json:"type"`
							Content string `json:"content"`
						} `json:"buttons"`
						Status string `json:"status"`
					} `json:"action"`
				} `json:"cta,omitempty"`
				Actor struct {
					Type string      `json:"type"`
					Data interface{} `json:"data"`
				} `json:"actor,omitempty"`
				Preheader  string `json:"preheader,omitempty"`
				SenderName string `json:"senderName,omitempty"`
			} `json:"template"`
			ReplyCallback struct {
				Active bool `json:"active"`
			} `json:"replyCallback,omitempty"`
		} `json:"step"`
		NotificationId   string    `json:"_notificationId"`
		Type             string    `json:"type"`
		CreatedAt        time.Time `json:"createdAt"`
		UpdatedAt        time.Time `json:"updatedAt"`
		ExecutionDetails []struct {
			Id         string    `json:"_id"`
			JobId      string    `json:"_jobId"`
			Detail     string    `json:"detail"`
			Source     string    `json:"source"`
			Status     string    `json:"status"`
			IsTest     bool      `json:"isTest"`
			IsRetry    bool      `json:"isRetry"`
			CreatedAt  time.Time `json:"createdAt"`
			UpdatedAt  time.Time `json:"updatedAt"`
			Id1        string    `json:"id"`
			Raw        string    `json:"raw,omitempty"`
			ProviderId string    `json:"providerId,omitempty"`
		} `json:"executionDetails"`
		Id1        string `json:"id"`
		ProviderId string `json:"providerId,omitempty"`
	} `json:"jobs"`
	Id1 string `json:"id"`
}

type Notifications struct {
	Page     int            `json:"page"`
	HasMore  bool           `json:"hasMore"`
	PageSize int            `json:"pageSize"`
	Data     []Notification `json:"data"`
}
type NotificationStatistics struct {
	Data []struct {
		Id        string   `json:"_id"`
		Count     int      `json:"count"`
		Templates []string `json:"templates"`
		Channels  []string `json:"channels"`
	} `json:"data"`
}

var (
	NotificationCounter = promauto.NewCounterVec(prometheus.CounterOpts{
		Namespace: "dbaas",
		Subsystem: "billing",
		Name:      "notification",
	}, []string{"action"})
	InAppMessageCounter = promauto.NewCounterVec(prometheus.CounterOpts{
		Namespace: "dbaas",
		Subsystem: "billing",
		Name:      "message",
	}, []string{"action"})
)

func (m NovuHandlerImpl) GetNotificationsByWorkflowID(query forms.NotificationQuery) (forms.OpsNotifications, error) {
	var resp Notifications
	var result forms.OpsNotifications
	channels := []string{NovuInAPP}
	inputArray := map[string][]string{"channels": channels}

	input := map[string]string{"page": strconv.Itoa(query.Page)}
	if len(query.TransactionID) > 0 {
		input["transactionId"] = query.TransactionID
	}
	err := GetClient().GetByArray("/v1/notifications", input, inputArray, &resp)
	if err != nil {
		return result, err
	}
	result.HasMore = resp.HasMore
	result.PageQuery.Page = resp.Page
	result.PageQuery.PageSize = resp.PageSize
	var notificationGroup string
	var operator string
	var ok bool
	var targetName string
	var targetRoles string
	var sendAt time.Time

	for _, event := range resp.Data {
		if nil != event.Payload {
			notificationGroup, ok = event.Payload[NovuParamNotificationGroup].(string)
			if !ok {
				notificationGroup = "null"
			}
			operator, ok = event.Payload[NovuParamOperator].(string)
			if !ok {
				operator = "null"
			}
			targetName, ok = event.Payload[NovuTargetName].(string)
			if !ok {
				targetName = event.To.SubscriberId
			}
			targetRoles, ok = event.Payload[NovuTargetRoles].(string)
			if !ok {
				targetRoles = event.To.SubscriberId
			}
			sendAtString, ok := event.Payload[NovuSendAt].(string)
			if !ok {
				sendAt = event.UpdatedAt
			} else {
				sendAt, err = time.Parse("2006-01-02T15:04:05.000Z0700", sendAtString)
				if err != nil {
					log.Info("GetNotificationsByWorkflowID err", zap.Error(err), zap.String("sendAtString", sendAtString))

				}
			}
		}
		result.Data = append(result.Data, forms.OpsNotification{
			Roles: targetRoles,
			Notification: forms.Notification{
				WorkFlowName:      event.Template.Name,
				NotificationGroup: notificationGroup,
				SubscriberID:      targetName,
				Status:            event.Jobs[0].Status + "_" + event.Jobs[1].Status,
				TransactionID:     event.TransactionId,
				SendAt:            sendAt,
				SendBy:            operator,
			},
		})
	}

	return result, nil
}
func (m NovuHandlerImpl) GetNotificationsSummaryByWorkflowID(workflowID string) ([]forms.NotificationsSummary, error) {
	var resp NotificationStatistics
	var result []forms.NotificationsSummary

	input := map[string]string{"days": strconv.Itoa(1000)}
	err := GetClient().GetByArray("/v1/notifications/graph/stats", input, nil, &resp)
	if err != nil {
		return result, err
	}
	for _, data := range resp.Data {
		for _, id := range data.Templates {
			if id == workflowID {
				result = append(result, forms.NotificationsSummary{
					Date:     data.Id,
					Count:    data.Count,
					Channels: []string{NovuInAPP},
				})
			}
		}

	}

	return result, nil
}

type NotificationGroupList struct {
	Data []NotificationGroup `json:"data"`
}

func (m NovuHandlerImpl) GetNotificationGroupIDByName(name string) (string, error) {

	outPut := NotificationGroupList{}
	err := GetClient().Get("/v1/notification-groups", nil, &outPut)
	if err != nil {
		log.Info("GetNotificationGroupIDByName err", zap.Error(err), zap.String("name", name))
		return "", err
	}
	for _, notificationGroup := range outPut.Data {
		if notificationGroup.Name == name {
			return notificationGroup.Id, nil
		}
	}
	log.Info("GetNotificationGroupIDByName is null ", zap.Error(err), zap.String("name", name))
	return "", nil
}
func (m NovuHandlerImpl) GetNotificationGroupNameByID(id string) (string, error) {

	type NotificationGroupRes struct {
		Data NotificationGroup `json:"data"`
	}
	var outPut NotificationGroupRes

	err := GetClient().Get("/v1/notification-groups/"+id, nil, &outPut)
	if err != nil {
		log.Info("GetNotificationGroupNameByID err", zap.Error(err), zap.String("id", id))
		return "", err
	}
	log.Info("GetNotificationGroupNameByID ", zap.Any("outPut", outPut), zap.String("id", id))
	return outPut.Data.Name, nil

}
