package jsm

import (
	"context"
	"crypto/sha256"
	"encoding/json"
	"fmt"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/sqs"
	"github.com/tidbcloud/account/internel/dao/entity"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

type JsmEvent struct {
	Type             string           `json:"type"`
	RolebindingEvent RolebindingEvent `json:"rolebinding"`
	UserInfoEvent    UserInfoEvent    `json:"user_info"`
	OrgInfoEvent     OrgInfoEvent     `json:"org_info"`
	ProjectInfoEvent ProjectInfoEvent `json:"project_info"`
	MspInfoEvent     MspInfoEvent     `json:"msp_info"`
}

type RolebindingEvent struct {
	Role      string `json:"role"`
	UserId    uint64 `json:"user_id"`
	OrgId     uint64 `json:"org_id"`
	ProjectId uint64 `json:"project_id"`
}

type UserInfoEvent struct {
	UserId    uint64 `json:"user_id"`
	Email     string `json:"email"`
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
}

type OrgInfoEvent struct {
	OrgId       uint64 `json:"org_id"`
	TimeZone    string `json:"time_zone"`
	CompanyName string `json:"company_name"`
}
type ProjectInfoEvent struct {
	ProjectId uint64 `json:"project_id"`
	OrgId     uint64 `json:"org_id"`
}

type MspInfoEvent struct {
	MspOrgID  uint64 `json:"msp_org_id"`
	UserOrgID uint64 `json:"user_org_id"`
}

type EventType string

const (
	TYPE_REMOVE_FROM_ORG        EventType = "remove_from_org"
	TYPE_CREATE_ORG             EventType = "create_org"
	TYPE_CREATE_USER            EventType = "create_user"
	TYPE_CREATE_PROJECT         EventType = "create_project"
	TYPE_INVITE_USER_TO_ORG     EventType = "invite_user_to_org"
	TYPE_INVITE_USER_TO_PROJECT EventType = "invite_user_to_project"
	TYPE_REMOVE_FROM_PROJECT    EventType = "remove_from_project"

	TYPE_ADD_MSP_RELATION         EventType = "add_msp_relation"
	TYPE_REMOVE_MSP_RELATION      EventType = "remove_msp_relation"
	TYPE_ENABLE_MSP_NOTIFICATION  EventType = "enable_msp_notification"
	TYPE_DISABLE_MSP_NOTIFICATION EventType = "disable_msp_notification"
)

func (s *JsmClient) NotifyCreateUserMessage(ctx context.Context, user *entity.User) error {
	userInfoEvent := UserInfoEvent{UserId: uint64(user.ID), Email: user.Email, FirstName: user.FirstName, LastName: user.LastName}
	event := JsmEvent{Type: string(TYPE_CREATE_USER), UserInfoEvent: userInfoEvent}
	return s.sendSqsMessage(ctx, uint64(user.TenantID), &event)
}

func (s *JsmClient) NotifyEnableMspNotificationMessage(ctx context.Context, tenantID uint64) error {
	mspInfoEvent := MspInfoEvent{MspOrgID: tenantID}
	event := JsmEvent{Type: string(TYPE_ENABLE_MSP_NOTIFICATION), MspInfoEvent: mspInfoEvent}
	return s.sendSqsMessage(ctx, tenantID, &event)
}

func (s *JsmClient) NotifyLinkCustomerWithMspMessage(ctx context.Context, CustomerOrgID uint64, mspOrgId uint64) error {
	mspInfoEvent := MspInfoEvent{MspOrgID: mspOrgId, UserOrgID: CustomerOrgID}
	event := JsmEvent{Type: string(TYPE_ADD_MSP_RELATION), MspInfoEvent: mspInfoEvent}
	return s.sendSqsMessage(ctx, CustomerOrgID, &event)
}

func (s *JsmClient) NotifyUnlinkCustomerWithMspMessage(ctx context.Context, CustomerOrgID uint64, mspOrgId uint64) error {
	mspInfoEvent := MspInfoEvent{MspOrgID: mspOrgId, UserOrgID: CustomerOrgID}
	event := JsmEvent{Type: string(TYPE_REMOVE_MSP_RELATION), MspInfoEvent: mspInfoEvent}
	return s.sendSqsMessage(ctx, CustomerOrgID, &event)
}

func (s *JsmClient) NotifyDisableMspNotificationMessage(ctx context.Context, tenantID uint64) error {
	mspInfoEvent := MspInfoEvent{MspOrgID: tenantID}
	event := JsmEvent{Type: string(TYPE_DISABLE_MSP_NOTIFICATION), MspInfoEvent: mspInfoEvent}
	return s.sendSqsMessage(ctx, tenantID, &event)
}

func (s *JsmClient) NotifyCreateOrgMessage(ctx context.Context, tenant *entity.Tenant) error {
	orgInfoEvent := OrgInfoEvent{OrgId: uint64(tenant.ID), TimeZone: tenant.TimeZone, CompanyName: tenant.Alias}
	event := JsmEvent{Type: string(TYPE_CREATE_ORG), OrgInfoEvent: orgInfoEvent}
	return s.sendSqsMessage(ctx, uint64(tenant.ID), &event)
}
func (s *JsmClient) NotifyCreateProjectMessage(ctx context.Context, projectId uint64, orgId uint64) error {
	projectInfoEvent := ProjectInfoEvent{ProjectId: projectId, OrgId: orgId}
	event := JsmEvent{Type: string(TYPE_CREATE_PROJECT), ProjectInfoEvent: projectInfoEvent}
	return s.sendSqsMessage(ctx, orgId, &event)
}

func (s *JsmClient) NotifyInviteUserToProjectMessage(ctx context.Context, projectId uint64, userId uint64) error {
	rolebindingEvent := RolebindingEvent{Role: "PROJECT_ROLE", ProjectId: projectId, UserId: userId}
	event := JsmEvent{Type: string(TYPE_INVITE_USER_TO_PROJECT), RolebindingEvent: rolebindingEvent}
	return s.sendSqsMessage(ctx, projectId, &event)
}

func (s *JsmClient) NotifyRemoveUserFromProjectMessage(ctx context.Context, projectId uint64, userId uint64) error {
	rolebindingEvent := RolebindingEvent{ProjectId: projectId, UserId: userId}
	event := JsmEvent{Type: string(TYPE_REMOVE_FROM_PROJECT), RolebindingEvent: rolebindingEvent}
	return s.sendSqsMessage(ctx, projectId, &event)
}
func (s *JsmClient) NotifyInviteUserToOrgMessage(ctx context.Context, orgId uint64, userId uint64, role string) error {
	rolebindingEvent := RolebindingEvent{Role: role, OrgId: orgId, UserId: userId}
	event := JsmEvent{Type: string(TYPE_INVITE_USER_TO_ORG), RolebindingEvent: rolebindingEvent}
	return s.sendSqsMessage(ctx, orgId, &event)
}

func (s *JsmClient) NotifyRemoveUserFromOrgMessage(ctx context.Context, orgId uint64, userId uint64) error {
	rolebindingEvent := RolebindingEvent{OrgId: orgId, UserId: userId}
	event := JsmEvent{Type: string(TYPE_REMOVE_FROM_ORG), RolebindingEvent: rolebindingEvent}
	return s.sendSqsMessage(ctx, orgId, &event)
}

func (s *JsmClient) sendSqsMessage(ctx context.Context, groupId uint64, event *JsmEvent) error {
	data, _ := json.Marshal(event)
	message := string(data)
	sqsUrl := s.conf.JobQueueUrl
	sendParams := &sqs.SendMessageInput{
		MessageBody:            aws.String(message),
		QueueUrl:               aws.String(sqsUrl),
		MessageGroupId:         aws.String(fmt.Sprint(groupId)),
		MessageDeduplicationId: aws.String(fmt.Sprintf("%x", sha256.Sum256([]byte(message)))),
	}
	_, err := s.sqs.SendMessage(sendParams)
	if err != nil {
		tilog.Error(ctx, "Failed to send JSM SQS message", zap.String("message", message), zap.Any("err", err))
		return err
	}
	tilog.Info(ctx, "Succeed to send JSM SQS message.", zap.String("message", message))
	return nil
}

func (s *JsmClient) NotifyTestMessage() error {
	ctx := context.Background()
	sqsUrl := s.conf.JobQueueUrl
	sendParams := &sqs.SendMessageInput{
		MessageBody:  aws.String("this is a test message"),
		QueueUrl:     aws.String(sqsUrl),
		DelaySeconds: aws.Int64(3),
	}
	_, err := s.sqs.SendMessage(sendParams)
	tilog.Info(ctx, "will send SQS message : test message", zap.String("url", sqsUrl))
	if err != nil {
		tilog.Error(ctx, "send SQS message error", zap.Any("error", err))
		return err
	}
	tilog.Info(ctx, "send SQS message success")
	return nil

}
