//go:generate ${GOBIN}/mockgen -destination notification_mock.go -package=notify ${GO_MODULE}/pkg/infra/larkcli/notify Notification
package notify

import (
	"context"
	"fmt"

	larkutil "github.com/tidbcloud/escalation-base/support-autoshift/pkg/infra/larkcli/util"
	"github.com/tidbcloud/escalation-base/support-autoshift/pkg/infra/log"
	"github.com/tidbcloud/escalation-base/support-autoshift/pkg/infra/utils/pointer"

	lark "github.com/larksuite/oapi-sdk-go/v3"
	larkim "github.com/larksuite/oapi-sdk-go/v3/service/im/v1"
)

const (
	ShareLinkPeriodWeek        = "week"
	ShareLinkPeriodDay         = "year"
	ShareLinkPeriodPermanently = "permanently"
)

type Param interface {
	Message() (larkutil.IMMessage, error)
}

type TextMessageParam string

func (r TextMessageParam) Message() (larkutil.IMMessage, error) {
	return larkutil.IMMessageText{
		Text: string(r),
	}, nil
}

type CreateChatParam struct {
	Name        string
	Description string
	OwnerID     string // bot is owner when empty
	UserIDs     []string
}

var (
	// Ref: https://open.feishu.cn/tool/cardbuilder
	msgCardSprintReleaseNotification = "ctp_AA6G5PFXLmYS"
	msgCardHotfixReleaseNotification = "ctp_AAmjqtgqhRkw"
	msgCardDeploymentNotification    = "ctp_AA6Jm8m8OcrG"
	msgCardReleaseCloseNotification  = "ctp_AAmV8y22GZu7"
	msgCardMaintenanceTaskRunning    = "ctp_AAqkQZo2tkF9"
	msgCardMaintenanceTaskClosed     = "ctp_AAq3aH1IUpCl"
	msgCardIncidentTopic             = "ctp_AAuwYYurkv6T"
	msgCardOperationNotify           = "ctp_AAw91UHnuwTV"
)

type Notification interface {
	// raw api
	SendMessage(ctx context.Context, chatID string, param Param) (*string /*message id*/, error)
	ReplyMessage(ctx context.Context, msgID string, param Param) (*string /*message id*/, error)
	ListMessages(ctx context.Context, chatID string, startTime string, endTime string) (*larkim.ListMessageRespData, error)
	GenerateChatShareLink(ctx context.Context, chatID string, validityPeriod string) (string /* link */, error)
}

func New(larkCli *lark.Client) Notification {
	cli := &client{
		larkCli: larkCli,
	}

	return cli
}

type client struct {
	larkCli *lark.Client
}

func (c *client) SendMessage(ctx context.Context, chatID string, param Param) (*string /*message id*/, error) {
	logger := log.FromContext(ctx)

	msg, err := param.Message()
	if err != nil {
		return nil, err
	}

	logger.V(4).Info("try to send a message", "message", msg, "chat_id", chatID)

	content, err := larkutil.EncodeIMMessage(msg)
	if err != nil {
		return nil, err
	}

	req := larkim.NewCreateMessageReqBuilder().
		ReceiveIdType("chat_id").
		Body(&larkim.CreateMessageReqBody{
			ReceiveId: pointer.Pointer(chatID),
			MsgType:   pointer.Pointer(msg.Type()),
			Content:   pointer.Pointer(content),
		}).
		Build()

	resp, err := c.larkCli.Im.Message.Create(ctx, req)
	if err != nil {
		return nil, fmt.Errorf("create message failed: %w", err)
	}
	if !resp.Success() {
		return nil, fmt.Errorf("create message failed: %s", resp.CodeError.String())
	}

	return resp.Data.MessageId, nil
}

func (c *client) ReplyMessage(ctx context.Context, msgID string, param Param) (*string /*message id*/, error) {
	logger := log.FromContext(ctx)

	msg, err := param.Message()
	if err != nil {
		return nil, err
	}

	logger.V(4).Info("try to reply a message", "message", msg, "msg_id", msgID)

	content, err := larkutil.EncodeIMMessage(msg)
	if err != nil {
		return nil, err
	}

	req := larkim.NewReplyMessageReqBuilder().
		MessageId(msgID).
		Body(&larkim.ReplyMessageReqBody{
			Content: &content,
			MsgType: pointer.Pointer(msg.Type()),
		}).
		Build()

	resp, err := c.larkCli.Im.Message.Reply(ctx, req)
	if err != nil {
		return nil, fmt.Errorf("reply message failed: %w", err)
	}
	if !resp.Success() {
		return nil, fmt.Errorf("reply message failed: %s", resp.CodeError.String())
	}

	return resp.Data.MessageId, nil
}

func (c *client) ListMessages(ctx context.Context, chatID string, startTime string, endTime string) (*larkim.ListMessageRespData, error) {
	req := larkim.NewListMessageReqBuilder().
		ContainerIdType("chat").
		ContainerId(chatID).
		StartTime(startTime).
		EndTime(endTime).
		SortType(`ByCreateTimeAsc`).
		PageSize(20).
		Build()
	resp, err := c.larkCli.Im.Message.List(ctx, req)
	if resp.Code != 0 {
		return nil, fmt.Errorf("list messages error, %v", resp.Msg)
	}
	if err != nil {
		return nil, err
	}

	if !resp.Success() {
		return nil, err
	}

	return resp.Data, nil
}

func (c *client) GenerateChatShareLink(ctx context.Context, chatID string, linkPeriod string) (string /* link */, error) {
	req := larkim.NewLinkChatReqBuilder().
		ChatId(chatID).
		Body(&larkim.LinkChatReqBody{
			ValidityPeriod: pointer.Pointer(linkPeriod),
		}).
		Build()

	resp, err := c.larkCli.Im.Chat.Link(ctx, req)
	if err != nil {
		return "", fmt.Errorf("gen chat link failed: %w", err)
	}
	if !resp.Success() {
		return "", fmt.Errorf("reply message failed: %s", resp.CodeError.String())
	}

	return *resp.Data.ShareLink, nil
}
