package share

import (
	"context"
	"net/http"

	"github.com/labstack/echo/v4"
	wechat_v2 "github.com/silenceper/wechat/v2"
	"github.com/silenceper/wechat/v2/cache"
	offConfig "github.com/silenceper/wechat/v2/officialaccount/config"
	"github.com/silenceper/wechat/v2/officialaccount/message"

	"github.com/chaitin/panda-wiki/consts"
	"github.com/chaitin/panda-wiki/domain"
	"github.com/chaitin/panda-wiki/handler"
	"github.com/chaitin/panda-wiki/log"
	"github.com/chaitin/panda-wiki/usecase"
)

type ShareAppHandler struct {
	*handler.BaseHandler
	logger  *log.Logger
	usecase *usecase.AppUsecase
}

func NewShareAppHandler(
	e *echo.Echo,
	baseHandler *handler.BaseHandler,
	logger *log.Logger,
	usecase *usecase.AppUsecase,
) *ShareAppHandler {
	h := &ShareAppHandler{
		BaseHandler: baseHandler,
		logger:      logger.WithModule("handler.share.app"),
		usecase:     usecase,
	}

	share := e.Group("share/v1/app",
		func(next echo.HandlerFunc) echo.HandlerFunc {
			return func(c echo.Context) error {
				c.Response().Header().Set("Access-Control-Allow-Origin", "*")
				c.Response().Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
				c.Response().Header().Set("Access-Control-Allow-Headers", "Content-Type, Origin, Accept")
				if c.Request().Method == "OPTIONS" {
					return c.NoContent(http.StatusOK)
				}
				return next(c)
			}
		})
	share.GET("/web/info", h.GetWebAppInfo)
	share.GET("/widget/info", h.GetWidgetAppInfo)
	share.GET("/wechat/info", h.WechatAppInfo)

	// wechat official account
	share.GET("/wechat/official_account", h.VerifyUrlWechatOfficialAccount)
	share.POST("/wechat/official_account", h.WechatHandlerOfficialAccount)
	return h
}

// GetWebAppInfo
//
//	@Summary		GetAppInfo
//	@Description	GetAppInfo
//	@Tags			share_app
//	@Accept			json
//	@Produce		json
//	@Param			X-KB-ID	header		string	true	"kb id"
//	@Success		200		{object}	domain.Response{data=domain.AppInfoResp}
//	@Router			/share/v1/app/web/info [get]
func (h *ShareAppHandler) GetWebAppInfo(c echo.Context) error {
	kbID := c.Request().Header.Get("X-KB-ID")
	if kbID == "" {
		return h.NewResponseWithError(c, "kb_id is required", nil)
	}
	ctx := context.WithValue(c.Request().Context(), consts.ContextKeyEdition, consts.GetLicenseEdition(c))
	appInfo, err := h.usecase.ShareGetWebAppInfo(ctx, kbID, domain.GetAuthID(c))
	if err != nil {
		return h.NewResponseWithError(c, err.Error(), err)
	}
	return h.NewResponseWithData(c, appInfo)
}

// GetWidgetAppInfo
//
//	@Summary		GetWidgetAppInfo
//	@Description	GetWidgetAppInfo
//	@Tags			share_app
//	@Accept			json
//	@Produce		json
//	@Param			X-KB-ID	header		string	true	"kb id"
//	@Success		200		{object}	domain.Response
//	@Router			/share/v1/app/widget/info [get]
func (h *ShareAppHandler) GetWidgetAppInfo(c echo.Context) error {
	kbID := c.Request().Header.Get("X-KB-ID")
	if kbID == "" {
		return h.NewResponseWithError(c, "kb_id is required", nil)
	}
	appInfo, err := h.usecase.GetWidgetAppInfo(c.Request().Context(), kbID)
	if err != nil {
		return h.NewResponseWithError(c, err.Error(), err)
	}
	return h.NewResponseWithData(c, appInfo)
}

// WechatAppInfo
//
//	@Summary		WechatAppInfo
//	@Description	WechatAppInfo
//	@Tags			share_chat
//	@Accept			json
//	@Produce		json
//	@Param			X-KB-ID	header		string	true	"kb id"
//	@Success		200		{object}	domain.Response{data=v1.WechatAppInfoResp}
//	@Router			/share/v1/app/wechat/info [get]
func (h *ShareAppHandler) WechatAppInfo(c echo.Context) error {
	kbID := c.Request().Header.Get("X-KB-ID")
	if kbID == "" {
		return h.NewResponseWithError(c, "kb_id is required", nil)
	}
	appInfo, err := h.usecase.GetWechatAppInfo(c.Request().Context(), kbID)
	if err != nil {
		return h.NewResponseWithError(c, err.Error(), err)
	}
	return h.NewResponseWithData(c, appInfo)
}

func (h *ShareAppHandler) VerifyUrlWechatOfficialAccount(c echo.Context) error {
	kbID := c.Request().Header.Get("X-KB-ID")

	if kbID == "" {
		return h.NewResponseWithError(c, "kb_id is required", nil)
	}
	ctx := c.Request().Context()

	// get wechat official account info
	appInfo, err := h.usecase.GetAppDetailByKBIDAndAppType(ctx, kbID, domain.AppTypeWechatOfficialAccount)
	if err != nil {
		h.logger.Error("get app detail failed")
		return h.NewResponseWithError(c, "GetAppDetailByKBIDAndAppType failed", err)
	}

	if appInfo.Settings.WechatOfficialAccountIsEnabled != nil && !*appInfo.Settings.WechatOfficialAccountIsEnabled {
		return h.NewResponseWithError(c, "wechat official account is not enabled", err)
	}
	wc := wechat_v2.NewWechat()
	memory := cache.NewMemory()
	cfg := &offConfig.Config{
		AppID:          appInfo.Settings.WechatOfficialAccountAppID,
		AppSecret:      appInfo.Settings.WechatOfficialAccountAppSecret,
		Token:          appInfo.Settings.WechatOfficialAccountToken,
		EncodingAESKey: appInfo.Settings.WechatOfficialAccountEncodingAESKey,
		Cache:          memory,
	}
	officialAccount := wc.GetOfficialAccount(cfg)
	server := officialAccount.GetServer(c.Request(), c.Response().Writer)

	// success
	err = server.Serve()
	if err != nil {
		return h.NewResponseWithError(c, "serve message failed", err)
	}
	return nil
}

func (h *ShareAppHandler) WechatHandlerOfficialAccount(c echo.Context) error {
	kbID := c.Request().Header.Get("X-KB-ID")

	if kbID == "" {
		return h.NewResponseWithError(c, "kb_id is required", nil)
	}
	ctx := c.Request().Context()

	// get wechat official account info
	appInfo, err := h.usecase.GetAppDetailByKBIDAndAppType(ctx, kbID, domain.AppTypeWechatOfficialAccount)
	if err != nil {
		h.logger.Error("get app detail failed")
		return h.NewResponseWithError(c, "GetAppDetailByKBIDAndAppType failed", err)
	}

	if appInfo.Settings.WechatOfficialAccountIsEnabled != nil && !*appInfo.Settings.WechatOfficialAccountIsEnabled {
		return h.NewResponseWithError(c, "wechat official account is not enabled", err)
	}
	wc := wechat_v2.NewWechat()
	memory := cache.NewMemory()
	cfg := &offConfig.Config{
		AppID:          appInfo.Settings.WechatOfficialAccountAppID,
		AppSecret:      appInfo.Settings.WechatOfficialAccountAppSecret,
		Token:          appInfo.Settings.WechatOfficialAccountToken,
		EncodingAESKey: appInfo.Settings.WechatOfficialAccountEncodingAESKey,
		Cache:          memory,
	}
	officialAccount := wc.GetOfficialAccount(cfg)
	server := officialAccount.GetServer(c.Request(), c.Response().Writer)

	// message handler
	server.SetMessageHandler(func(msg *message.MixMessage) *message.Reply {
		h.logger.Info("received message:", log.Any("msgtype", msg.MsgType), log.Any("fromUserName", msg.FromUserName), log.String("content", msg.Content), log.Any("event type", msg.Event))

		switch msg.MsgType {
		case message.MsgTypeText:
			// text消息
			userOpenID := msg.FromUserName
			userContent := msg.Content
			h.logger.Info("user_open_id user_content", log.Any("user_open_id", userOpenID), log.Any("user content", userContent))
			// 异步发送
			go func(openID, content string) {
				ctx := context.Background()
				// send content to ai
				result, err := h.usecase.GetWechatOfficialAccountResponse(ctx, officialAccount, kbID, openID, content)
				if err != nil {
					h.logger.Error("get wechat official account response failed", log.Error(err))
					return
				}
				// send response to user --> 需要开启客服消息权限
				err = h.usecase.SendCustomerServiceMessage(officialAccount, string(userOpenID), result)
				if err != nil {
					h.logger.Error("send to customer service failed", log.Error(err))
				}
			}(string(userOpenID), userContent)
			return &message.Reply{MsgType: message.MsgTypeText, MsgData: message.NewText("您的问题已经收到，正在努力思考中，请稍候...")}
		case message.MsgTypeEvent:
			if msg.Event == message.EventSubscribe {
				return &message.Reply{MsgType: message.MsgTypeText, MsgData: message.NewText("感谢关注,欢迎提问！")} // 立即回复简单信息
			}
			return nil
		default:
			h.logger.Info("unknown message type", log.Any("message type", msg.MsgType))
			return &message.Reply{MsgType: message.MsgTypeText, MsgData: message.NewText("未知消息类型，请发送正确的类型...")}
		}
	})

	// success
	err = server.Serve()
	if err != nil {
		h.logger.Error("serve message failed", log.Error(err))
		return h.NewResponseWithError(c, "serve message failed", err)
	}

	// send message to user
	err = server.Send()
	if err != nil {
		h.logger.Error("send message failed", log.Error(err))
		return h.NewResponseWithError(c, "send message failed", err)
	}
	return nil
}
