// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package slackcmd

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"

	"github.com/shomali11/slacker"

	"github.com/openimsdk/openkf/server/internal/conn/client"
	requestparams "github.com/openimsdk/openkf/server/internal/params/request"
	"github.com/openimsdk/openkf/server/internal/service"
	"github.com/openimsdk/openkf/server/pkg/log"
)

// SLACK_PERFIX do not use separator.
const SLACK_PERFIX = "SLACK"

// BOT_CONTEXT_MAP bot context map in memory. TODO: use better way to store this map.
var BOT_CONTEXT_MAP map[string]slacker.BotContext

// InitSlackListen init slack socket listen.
func InitSlackListen() {
	bot := client.InitSlack()

	// init bot context map
	BOT_CONTEXT_MAP = make(map[string]slacker.BotContext)
	ctx := context.Background()
	svc := service.NewSlackService(ctx)

	// receive all @ message
	bot.Command("<question>", &slacker.CommandDefinition{
		Handler: func(bc slacker.BotContext, r slacker.Request, w slacker.ResponseWriter) {
			BOT_CONTEXT_MAP[bc.Event().ChannelID] = bc

			// find or create a customer
			messageEvent := bc.Event()
			slackUserId := fmt.Sprintf("%s%s", SLACK_PERFIX, messageEvent.UserID)
			senderId, _, err := svc.CreateCustomer(slackUserId, messageEvent.UserProfile)
			if err != nil {
				log.Errorf("Slack", "CreateCustomer failed: %s", err.Error())

				return
			}

			question := r.Param("question")
			// TODO: remove id from question
			words := strings.Split(question, " ")
			question = strings.Join(words[1:], " ")

			// send message to staff and cache pair
			err = svc.SendMsg(senderId, question, bc)
			if err != nil {
				log.Errorf("Slack", "SendMsg failed: %s", err.Error())
				_ = w.Reply("[OpenKF] Error in system.", slacker.WithThreadReply(true))
			}
		},
	})

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	if err := bot.Listen(ctx); err != nil {
		log.Panicf("Slack Client", "Connection failed: %s", err.Error())
	}
}

// SendMsg to slack user.
func SendMsg(params *requestparams.MsgAbstract) {
	recvID := params.RecvID
	content := params.Content
	ctx := context.Background()

	// Parse content {"content": "xxx"}
	type Content struct {
		Content string `json:"content"`
	}
	c := &Content{}
	err := json.Unmarshal([]byte(content), &c)
	if err != nil {
		log.Errorf("Slack", "SendMsg json.Unmarshal failed: %s", err.Error())

		return
	}

	udSvc := service.NewUserDispatchService(ctx)
	sMap := udSvc.GetSlackMap(recvID)
	slackCtx, ok := BOT_CONTEXT_MAP[sMap.SlackChannelID]
	if !ok {
		log.Errorf("Slack", "Slack context not found")

		return
	}

	// Reply in thread and return response
	err = slacker.NewResponse(slackCtx).Reply(c.Content, slacker.WithThreadReply(true))
	if err != nil {
		log.Errorf("Slack", "SendToSlack failed: %s", err.Error())
	}
}
