package rest

import (
	"errors"
	"net/http"

	"github.com/pingcap/agent-git-service/internal/db"
	"github.com/pingcap/agent-git-service/internal/rest/respond"
	"github.com/pingcap/agent-git-service/internal/service"
)

// MergePRAsync handles PUT /api/v3/repos/{owner}/{repo}/pulls/{pull_number}/merge-async.
func (d *Deps) MergePRAsync(w http.ResponseWriter, r *http.Request) {
	number, ok := mustIntParam(w, r, "pull_number")
	if !ok {
		return
	}
	var body struct {
		CommitTitle   string `json:"commit_title"`
		CommitMessage string `json:"commit_message"`
		SHA           string `json:"sha"`
		MergeMethod   string `json:"merge_method"`
		MergeAction   string `json:"merge_action"`
	}
	if err := decodeBodyStrictOptional(r, &body); err != nil {
		respond.ValidationFailed(w, "invalid body")
		return
	}
	result, err := d.Svc.SubmitAsyncMerge(r.Context(), repoFullName(r), number, service.SubmitAsyncMergeInput{
		CommitTitle: body.CommitTitle, CommitMessage: body.CommitMessage, SHA: body.SHA,
		MergeMethod: body.MergeMethod, MergeAction: body.MergeAction,
	})
	if err != nil {
		var notReady *service.AsyncMergeNotReadyError
		if errors.As(err, &notReady) {
			respond.JSON(w, http.StatusBadRequest, asyncMergeFailedJSON(notReady.Message))
			return
		}
		respond.ServiceErrorRequest(r, w, err)
		return
	}
	if result.AlreadyMerged {
		respond.JSON(w, http.StatusOK, asyncMergeMergedJSON(result.Request.ResultSHA, "Pull request is already merged."))
		return
	}
	if result.AlreadyQueued {
		respond.JSON(w, http.StatusOK, asyncMergeEnqueuedJSON("Pull request is already in the merge queue."))
		return
	}
	if result.ExistingActive {
		respond.JSON(w, http.StatusConflict, asyncMergePendingJSON(result.Request, "A merge request already exists for this pull request."))
		return
	}
	respond.JSON(w, http.StatusAccepted, asyncMergePendingJSON(result.Request, "Merge request enqueued."))
}

// GetPRAsyncMerge handles GET /api/v3/repos/{owner}/{repo}/pulls/{pull_number}/merge-async/{uuid}.
func (d *Deps) GetPRAsyncMerge(w http.ResponseWriter, r *http.Request) {
	number, ok := mustIntParam(w, r, "pull_number")
	if !ok {
		return
	}
	req, err := d.Svc.GetAsyncMerge(r.Context(), repoFullName(r), number, pathParam(r, "uuid"))
	if err != nil {
		respond.ServiceErrorRequest(r, w, err)
		return
	}
	respond.JSON(w, http.StatusOK, asyncMergeResultJSON(req))
}

func asyncMergeResultJSON(req db.AsyncMergeRequest) map[string]any {
	switch req.Status {
	case service.AsyncMergeMerged:
		return asyncMergeMergedJSON(req.ResultSHA, "Pull request was merged.")
	case service.AsyncMergeFailed:
		return asyncMergeFailedJSON(req.FailureMessage)
	case service.AsyncMergeEnqueued:
		return asyncMergeEnqueuedJSON("Pull request is in the merge queue.")
	default:
		return asyncMergePendingJSON(req, "Merge request is in progress.")
	}
}

func asyncMergePendingJSON(req db.AsyncMergeRequest, message string) map[string]any {
	return map[string]any{"status": service.AsyncMergePending, "details": map[string]any{
		"message": message, "uuid": req.UUID, "merge_method": req.MergeMethod,
		"merge_action": req.MergeAction, "expected_head_sha": req.ExpectedHeadSHA,
	}}
}

func asyncMergeEnqueuedJSON(message string) map[string]any {
	return map[string]any{"status": service.AsyncMergeEnqueued, "details": map[string]any{"message": message}}
}

func asyncMergeMergedJSON(sha, message string) map[string]any {
	return map[string]any{"status": service.AsyncMergeMerged, "details": map[string]any{"message": message, "sha": sha}}
}

func asyncMergeFailedJSON(message string) map[string]any {
	return map[string]any{"status": service.AsyncMergeFailed, "details": map[string]any{"message": message}}
}
