package jira

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"io"
	"log/slog"
	"net/http"

	"github.com/gin-gonic/gin"

	"github.com/tidbcloud/jiraport/pkg/clients/lambda"
)

type Jira struct {
	logger       *slog.Logger
	lambdaClient lambda.Interface
	secret       string
}

type JiraEvent struct {
	JiraWebhookEvent
	Receiver string `json:"receiver"`
}

// create new jira service and register route in gin router
func New(
	r *gin.RouterGroup,
	logger *slog.Logger,
	lambdaClient lambda.Interface,
	secret string,
) (*Jira, error) {
	j := &Jira{
		logger:       logger,
		lambdaClient: lambdaClient,
		secret:       secret,
	}

	r.POST("/webhook/:receiver", j.jiraEventHandler())
	return j, nil
}

// gin route handler, receive http post from jira and send event to resolver
func (j *Jira) jiraEventHandler() func(*gin.Context) {
	return func(ctx *gin.Context) {
		payload, err := io.ReadAll(ctx.Request.Body)
		if err != nil {
			ctx.JSON(400, gin.H{
				"message": "Failed to read payload",
			})
			return
		}
		provided := ctx.GetHeader("X-Hub-Signature")
		h := hmac.New(sha256.New, []byte(j.secret))
		h.Write(payload)
		hash := hex.EncodeToString(h.Sum(nil))
		if provided != "sha256="+hash {
			ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
			ctx.Abort()
			return
		}
		receiver := ctx.Param("receiver")
		var jiraWebhookEvent JiraWebhookEvent
		err = json.Unmarshal(payload, &jiraWebhookEvent)
		if err != nil {
			j.logger.Error("request body bind failed", "err", err)
			ctx.Status(http.StatusBadRequest)
			return
		}

		j.logger.Info("receive jira webhook event",
			"receiver", receiver,
			"issue", jiraWebhookEvent.Issue.Key)

		jiraEvent := JiraEvent{
			JiraWebhookEvent: jiraWebhookEvent,
			Receiver:         receiver,
		}
		payload, err = json.Marshal(jiraEvent)
		if err != nil {
			j.logger.Error("encode jira event failed",
				"err", err,
				"issue", jiraWebhookEvent.Issue.Key)
			ctx.Status(http.StatusBadRequest)
			return
		}

		event := lambda.Event{
			Name:    "JiraIssue",
			Payload: payload,
		}
		if err := j.lambdaClient.InvokeAsync(ctx, event); err != nil {
			j.logger.Error("invoke lambda function failed",
				"err", err,
				"lambdaEvent", event,
				"issue", jiraWebhookEvent.Issue.Key)
			ctx.Status(http.StatusInternalServerError)
			return
		}

		j.logger.Info("handle jira webhook event success",
			"issue", jiraWebhookEvent.Issue.Key)
		ctx.Status(http.StatusOK)
	}
}
