package httpserver

import (
	"errors"
	"net/http"
	"net/url"
	"strconv"

	apicontract "bit.admedia.com/scm/ad/adpilot-indexing-repo-sync.com/internal/contracts/api"
	"bit.admedia.com/scm/ad/adpilot-indexing-repo-sync.com/internal/git"
	"bit.admedia.com/scm/ad/adpilot-indexing-repo-sync.com/internal/service"
)

type errorResponse struct {
	Error string `json:"error"`
}

// SyncTrigger accepts repository sync requests for background processing.
type SyncTrigger interface {
	TriggerSync(repoID, ref string) (jobID string, err error)
}

// ForceSyncTrigger accepts force sync requests for background processing.
type ForceSyncTrigger interface {
	TriggerForceSync(repoID, ref string, forceOverride bool) (service.ForceSyncAccepted, error)
}

func handleAdminRepoSync(trigger SyncTrigger) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		repoID, err := url.PathUnescape(r.PathValue("repo_id"))
		if err != nil {
			writeJSON(w, http.StatusBadRequest, errorResponse{Error: "invalid repo_id path"})
			return
		}
		if err := git.ValidateRepoID(repoID); err != nil {
			writeJSON(w, http.StatusBadRequest, errorResponse{Error: err.Error()})
			return
		}

		ref := r.URL.Query().Get("ref")

		jobID, err := trigger.TriggerSync(repoID, ref)
		if err != nil {
			writeJSON(w, http.StatusInternalServerError, errorResponse{Error: "failed to enqueue sync"})
			return
		}

		if ref == "" {
			ref = service.DefaultRef
		}

		writeJSON(w, http.StatusAccepted, apicontract.SyncAcceptedResponse{
			RepoID: repoID,
			Ref:    ref,
			JobID:  jobID,
			Status: "accepted",
		})
	}
}

func handleAdminRepoForceSync(trigger ForceSyncTrigger) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		repoID, err := url.PathUnescape(r.PathValue("repo_id"))
		if err != nil {
			writeJSON(w, http.StatusBadRequest, errorResponse{Error: "invalid repo_id path"})
			return
		}
		if err := git.ValidateRepoID(repoID); err != nil {
			writeJSON(w, http.StatusBadRequest, errorResponse{Error: err.Error()})
			return
		}

		ref := r.URL.Query().Get("ref")
		forceOverride, _ := strconv.ParseBool(r.URL.Query().Get("force"))

		result, err := trigger.TriggerForceSync(repoID, ref, forceOverride)
		if err != nil {
			switch {
			case errors.Is(err, service.ErrForceSyncBusy),
				errors.Is(err, service.ErrForceSyncNotAllowed):
				writeJSON(w, http.StatusConflict, errorResponse{Error: err.Error()})
			default:
				writeJSON(w, http.StatusInternalServerError, errorResponse{Error: err.Error()})
			}
			return
		}

		writeJSON(w, http.StatusAccepted, apicontract.ForceSyncAcceptedResponse{
			RepoID: result.RepoID,
			Ref:    result.Ref,
			JobID:  result.JobID,
			Status: "accepted",
			Mode:   result.Mode,
			Purged: result.Purged,
		})
	}
}

var _ SyncTrigger = (*service.SyncService)(nil)
var _ ForceSyncTrigger = (*service.SyncService)(nil)