package message

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/url"

	lark "github.com/larksuite/oapi-sdk-go/v3"
	larkcore "github.com/larksuite/oapi-sdk-go/v3/core"
	larkim "github.com/larksuite/oapi-sdk-go/v3/service/im/v1"
	larkutil "github.com/tidbcloud/escalation-base/feishu_messager_v2/pkg/infra/clients/larkcli/util"
	"github.com/tidbcloud/escalation-base/feishu_messager_v2/pkg/infra/log"
	"github.com/tidbcloud/escalation-base/feishu_messager_v2/pkg/infra/utils/pointer"
)

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

	return cli
}

type client struct {
	larkCli *lark.Client
}

// SendMessage sends a message to a chat/user
//
// receiveType: open_id/user_id/union_id/email/chat_id
func (c *client) SendMessage(ctx context.Context, receiveType, receiveID 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, "receive_type", receiveType, "receive_id", receiveID)

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

	req := larkim.NewCreateMessageReqBuilder().
		ReceiveIdType(receiveType).
		Body(&larkim.CreateMessageReqBody{
			ReceiveId: pointer.Pointer(receiveID),
			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())
	}

	//logger.Info("sent message", "status_code", resp.StatusCode, "response", string(resp.RawBody))
	return resp.Data.MessageId, nil
}

func (c *client) SendMessageViaWebhook(ctx context.Context, webhookURL string, param Param) error {
	logger := log.FromContext(ctx)

	u, err := url.Parse(webhookURL)
	if err != nil {
		return fmt.Errorf("invalid webhook url: %w", err)
	}

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

	body := larkutil.LarkWebhookMessageReq{
		MsgType: msg.Type(),
		Card:    msg,
	}
	logger.V(4).Info("try to send a message via webhook", "body", body)

	resp, err := c.larkCli.Post(ctx, u.Path, body, larkcore.AccessTokenTypeNone)
	if err != nil {
		return fmt.Errorf("failed to send message via webhook: %w", err)
	}

	respErr := larkcore.CodeError{}
	err = json.Unmarshal(resp.RawBody, &respErr)
	if err != nil {
		return fmt.Errorf("failed to unmarshal response body: %w", err)
	}

	if respErr.Code != 0 {
		return fmt.Errorf("failed to send message via webhook: %s", string(resp.RawBody))
	}

	//logger.Info("sent message via webhook", "status_code", resp.StatusCode, "response", string(resp.RawBody))
	return nil
}

func (c *client) MakeUrgent(ctx context.Context, messageId string, userId string, urgentType string) error {
	switch urgentType {
	case "in-app":
		req := larkim.NewUrgentAppMessageReqBuilder().
			MessageId(messageId).
			UserIdType("user_id"). // currently only `user_id` is supported
			UrgentReceivers(larkim.NewUrgentReceiversBuilder().
				UserIdList([]string{userId}).
				Build()).
			Build()
		resp, err := c.larkCli.Im.Message.UrgentApp(context.Background(), req)
		if err != nil {
			return fmt.Errorf("call UrgentApp() failed: %w ", err)
		} else if !resp.Success() {
			return fmt.Errorf("call UrgentApp() not succeed. respCode: %d, respMsg: %s", resp.Code, resp.Msg)
		} else {
			return nil
		}
	case "sms":
		req := larkim.NewUrgentSmsMessageReqBuilder().
			MessageId(messageId).
			UserIdType("user_id").
			UrgentReceivers(larkim.NewUrgentReceiversBuilder().
				UserIdList([]string{userId}).
				Build()).
			Build()
		resp, err := c.larkCli.Im.Message.UrgentSms(context.Background(), req)
		if err != nil {
			return fmt.Errorf("call UrgentSms() failed: %w ", err)
		} else if !resp.Success() {
			return fmt.Errorf("call UrgentSms() not succeed. respCode: %d, respMsg: %s", resp.Code, resp.Msg)
		} else {
			return nil
		}
	case "phone":
		req := larkim.NewUrgentPhoneMessageReqBuilder().
			MessageId(messageId).
			UserIdType("user_id").
			UrgentReceivers(larkim.NewUrgentReceiversBuilder().
				UserIdList([]string{userId}).
				Build()).
			Build()
		resp, err := c.larkCli.Im.Message.UrgentPhone(context.Background(), req)
		if err != nil {
			return fmt.Errorf("call UrgentPhone() failed: %w ", err)
		} else if !resp.Success() {
			return fmt.Errorf("call UrgentPhone() not succeed. respCode: %d, respMsg: %s", resp.Code, resp.Msg)
		} else {
			return nil
		}
	default:
		return fmt.Errorf("non-support urgent type: %s", urgentType)
	}

}

func (c *client) ReadUsers(ctx context.Context, messageId string) ([]string, error) {
	req := larkim.NewReadUsersMessageReqBuilder().MessageId(messageId).UserIdType("user_id").Build()
	resp, err := c.larkCli.Im.Message.ReadUsers(ctx, req)

	if err != nil {
		return []string{}, err
	}

	if !resp.Success() {
		return []string{}, errors.New(fmt.Sprint(resp.Code) + ": " + resp.Msg)
	}

	readUsers := []string{}
	for _, user := range resp.Data.Items {
		readUsers = append(readUsers, *user.UserId)
	}

	return readUsers, nil
}
