package httpapi

import (
	"context"
	"crypto/sha256"
	"encoding/json"
	"errors"
	"net/http"
	"strings"
	"time"

	"github.com/easyai/easyai-ai-gateway/apps/api/internal/auth"
	"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
)

const (
	acceptanceRunHeader              = "X-EasyAI-Acceptance-Run"
	acceptanceTokenHeader            = "X-EasyAI-Acceptance-Token"
	acceptanceUpstreamHeader         = "X-EasyAI-Acceptance-Upstream"
	taskTrafficAuthorizationCacheTTL = 5 * time.Second
)

type taskTrafficAuthorizationCacheKey struct {
	runID     string
	apiKeyID  string
	userID    string
	tokenHash [sha256.Size]byte
}

type taskTrafficAuthorizationCacheEntry struct {
	runID     string
	expiresAt time.Time
}

type taskTrafficAuthorizationCall struct {
	done  chan struct{}
	runID string
	err   error
}

type taskTrafficAdmission struct {
	RunMode         string
	AcceptanceRunID string
}

type taskTrafficError struct {
	Status  int
	Code    string
	Message string
	Err     error
}

func (s *Server) acceptanceStore() *store.Store {
	if s.coordinationStore != nil {
		return s.coordinationStore
	}
	return s.store
}

func (e *taskTrafficError) Error() string {
	return e.Message
}

func (e *taskTrafficError) ErrorCode() string {
	return e.Code
}

func (s *Server) authorizeTaskTraffic(r *http.Request, user *auth.User) (taskTrafficAdmission, error) {
	runID, err := s.authorizeAcceptanceTask(r.Context(), r.Header.Get(acceptanceRunHeader), r.Header.Get(acceptanceTokenHeader), user)
	if err != nil {
		switch {
		case errors.Is(err, store.ErrProductionTrafficPaused):
			return taskTrafficAdmission{}, &taskTrafficError{
				Status: http.StatusServiceUnavailable, Code: "validation_in_progress",
				Message: "new production tasks are paused while validation is running", Err: err,
			}
		case errors.Is(err, store.ErrAcceptanceNotAuthorized):
			return taskTrafficAdmission{}, &taskTrafficError{
				Status: http.StatusForbidden, Code: "acceptance_not_authorized",
				Message: "acceptance credentials do not match the active run", Err: err,
			}
		case errors.Is(err, store.ErrAcceptanceRunNotActive):
			return taskTrafficAdmission{}, &taskTrafficError{
				Status: http.StatusConflict, Code: "acceptance_run_not_active",
				Message: "acceptance headers are not valid while live traffic is enabled", Err: err,
			}
		default:
			return taskTrafficAdmission{}, &taskTrafficError{
				Status: http.StatusServiceUnavailable, Code: "traffic_gate_unavailable",
				Message: "task traffic gate is unavailable", Err: err,
			}
		}
	}
	if runID != "" {
		runMode := "acceptance"
		if strings.EqualFold(strings.TrimSpace(r.Header.Get(acceptanceUpstreamHeader)), "real") {
			runMode = "acceptance_canary"
		}
		return taskTrafficAdmission{RunMode: runMode, AcceptanceRunID: runID}, nil
	}
	return taskTrafficAdmission{RunMode: "production"}, nil
}

// authorizeAcceptanceTask collapses concurrent validation requests for the same
// participant into one critical-pool lookup and briefly caches only successful
// validation grants. Live requests and incomplete acceptance credentials always
// consult PostgreSQL so a validation transition remains fail-closed.
func (s *Server) authorizeAcceptanceTask(ctx context.Context, runID string, token string, user *auth.User) (string, error) {
	runID = strings.TrimSpace(runID)
	token = strings.TrimSpace(token)
	if runID == "" || token == "" || user == nil ||
		strings.TrimSpace(user.APIKeyID) == "" || strings.TrimSpace(user.ID) == "" {
		return s.acceptanceStore().AuthorizeAcceptanceTask(ctx, runID, token, user)
	}
	key := taskTrafficAuthorizationCacheKey{
		runID: runID, apiKeyID: strings.TrimSpace(user.APIKeyID), userID: strings.TrimSpace(user.ID),
		tokenHash: sha256.Sum256([]byte(token)),
	}
	return s.cachedTaskTrafficAuthorization(ctx, key, taskTrafficAuthorizationCacheTTL, func() (string, error) {
		return s.acceptanceStore().AuthorizeAcceptanceTask(ctx, runID, token, user)
	})
}

func (s *Server) cachedTaskTrafficAuthorization(
	ctx context.Context,
	key taskTrafficAuthorizationCacheKey,
	ttl time.Duration,
	load func() (string, error),
) (string, error) {
	now := time.Now()
	s.taskTrafficCacheMu.Lock()
	if entry, ok := s.taskTrafficCache[key]; ok {
		if now.Before(entry.expiresAt) {
			s.taskTrafficCacheMu.Unlock()
			return entry.runID, nil
		}
		delete(s.taskTrafficCache, key)
	}
	if call, ok := s.taskTrafficInflight[key]; ok {
		s.taskTrafficCacheMu.Unlock()
		select {
		case <-call.done:
			return call.runID, call.err
		case <-ctx.Done():
			return "", ctx.Err()
		}
	}
	if s.taskTrafficInflight == nil {
		s.taskTrafficInflight = make(map[taskTrafficAuthorizationCacheKey]*taskTrafficAuthorizationCall)
	}
	call := &taskTrafficAuthorizationCall{done: make(chan struct{})}
	s.taskTrafficInflight[key] = call
	s.taskTrafficCacheMu.Unlock()

	call.runID, call.err = load()

	s.taskTrafficCacheMu.Lock()
	delete(s.taskTrafficInflight, key)
	if call.err == nil && call.runID != "" {
		if s.taskTrafficCache == nil {
			s.taskTrafficCache = make(map[taskTrafficAuthorizationCacheKey]taskTrafficAuthorizationCacheEntry)
		}
		callTTL := ttl
		if callTTL <= 0 {
			callTTL = taskTrafficAuthorizationCacheTTL
		}
		s.taskTrafficCache[key] = taskTrafficAuthorizationCacheEntry{
			runID: call.runID, expiresAt: time.Now().Add(callTTL),
		}
	}
	close(call.done)
	s.taskTrafficCacheMu.Unlock()
	return call.runID, call.err
}

func (s *Server) admittedTaskRunMode(admission taskTrafficAdmission, body map[string]any) (string, error) {
	if admission.RunMode == "acceptance" || admission.RunMode == "acceptance_canary" {
		return admission.RunMode, nil
	}
	requested := runModeFromRequest(body)
	requestedMode := strings.ToLower(strings.TrimSpace(requested))
	if requestedMode == "acceptance" || requestedMode == "acceptance_canary" {
		return "", &taskTrafficError{
			Status: http.StatusForbidden, Code: "acceptance_not_authorized",
			Message: "acceptance mode is available only through an active acceptance run",
		}
	}
	if strings.EqualFold(strings.TrimSpace(s.cfg.AppEnv), "production") && requestedMode == "simulation" {
		return "", &taskTrafficError{
			Status: http.StatusForbidden, Code: "simulation_not_authorized",
			Message: "simulation mode is available only through an active acceptance run",
		}
	}
	return requested, nil
}

func writeTaskTrafficError(w http.ResponseWriter, err error, protocol string) {
	var trafficErr *taskTrafficError
	if !errors.As(err, &trafficErr) {
		trafficErr = &taskTrafficError{
			Status: http.StatusServiceUnavailable, Code: "traffic_gate_unavailable",
			Message: "task traffic gate is unavailable", Err: err,
		}
	}
	if protocol != "" {
		writeProtocolError(w, protocol, trafficErr.Status, trafficErr.Message, nil, trafficErr.Code)
		return
	}
	writeErrorWithDetails(w, trafficErr.Status, trafficErr.Message, nil, trafficErr.Code)
}

// getGatewayTrafficMode godoc
// @Summary 获取 Gateway 流量模式
// @Tags acceptance
// @Produce json
// @Security BearerAuth
// @Success 200 {object} store.GatewayTrafficMode
// @Router /api/admin/system/acceptance/traffic-mode [get]
func (s *Server) getGatewayTrafficMode(w http.ResponseWriter, r *http.Request) {
	mode, err := s.acceptanceStore().GetGatewayTrafficMode(r.Context())
	if err != nil {
		writeError(w, http.StatusInternalServerError, "get gateway traffic mode failed")
		return
	}
	writeJSON(w, http.StatusOK, mode)
}

// pauseGatewayTraffic godoc
// @Summary 上线后硬门禁异常时通过 CAS 暂停正式新任务
// @Tags acceptance
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param body body store.PauseGatewayTrafficInput true "当前线上 release 与 revision CAS"
// @Success 200 {object} store.GatewayTrafficMode
// @Router /api/admin/system/acceptance/traffic-mode/pause [post]
func (s *Server) pauseGatewayTraffic(w http.ResponseWriter, r *http.Request) {
	var input store.PauseGatewayTrafficInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeError(w, http.StatusBadRequest, "invalid json body")
		return
	}
	mode, err := s.acceptanceStore().PauseGatewayTraffic(r.Context(), input)
	if err != nil {
		writeAcceptanceMutationError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, mode)
}

// listCapacityProfiles godoc
// @Summary 获取经生产同构验收认证的容量配置
// @Tags acceptance
// @Produce json
// @Security BearerAuth
// @Success 200 {array} store.CapacityProfile
// @Router /api/admin/system/acceptance/capacity-profiles [get]
func (s *Server) listCapacityProfiles(w http.ResponseWriter, r *http.Request) {
	profiles, err := s.acceptanceStore().ListCapacityProfiles(r.Context())
	if err != nil {
		writeError(w, http.StatusInternalServerError, "list capacity profiles failed")
		return
	}
	writeJSON(w, http.StatusOK, profiles)
}

// getWorkerClusterRuntime godoc
// @Summary 获取集群 Worker 实时负载与领取容量
// @Tags acceptance
// @Produce json
// @Security BearerAuth
// @Success 200 {object} store.WorkerClusterRuntime
// @Router /api/admin/runtime/workers [get]
func (s *Server) getWorkerClusterRuntime(w http.ResponseWriter, r *http.Request) {
	runtime, err := s.acceptanceStore().GetWorkerClusterRuntime(r.Context())
	if err != nil {
		writeError(w, http.StatusInternalServerError, "get worker cluster runtime failed")
		return
	}
	writeJSON(w, http.StatusOK, runtime)
}

// createAcceptanceRun godoc
// @Summary 创建生产同构验收 Run
// @Tags acceptance
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param body body store.CreateAcceptanceRunInput true "验收 Run"
// @Success 201 {object} store.AcceptanceRun
// @Router /api/admin/system/acceptance/runs [post]
func (s *Server) createAcceptanceRun(w http.ResponseWriter, r *http.Request) {
	var input store.CreateAcceptanceRunInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeError(w, http.StatusBadRequest, "invalid json body")
		return
	}
	run, err := s.acceptanceStore().CreateAcceptanceRun(r.Context(), input)
	if err != nil {
		writeError(w, http.StatusBadRequest, err.Error())
		return
	}
	writeJSON(w, http.StatusCreated, run)
}

// getAcceptanceRun godoc
// @Summary 获取生产同构验收 Run
// @Tags acceptance
// @Produce json
// @Security BearerAuth
// @Param runID path string true "验收 Run ID"
// @Success 200 {object} store.AcceptanceRun
// @Router /api/admin/system/acceptance/runs/{runID} [get]
func (s *Server) getAcceptanceRun(w http.ResponseWriter, r *http.Request) {
	run, err := s.acceptanceStore().GetAcceptanceRun(r.Context(), r.PathValue("runID"))
	if err != nil {
		if store.IsNotFound(err) {
			writeError(w, http.StatusNotFound, "acceptance run not found")
			return
		}
		writeError(w, http.StatusInternalServerError, "get acceptance run failed")
		return
	}
	writeJSON(w, http.StatusOK, run)
}

// activateAcceptanceRun godoc
// @Summary 切换到 validation 并激活 Run
// @Tags acceptance
// @Produce json
// @Security BearerAuth
// @Param runID path string true "验收 Run ID"
// @Success 200 {object} store.GatewayTrafficMode
// @Router /api/admin/system/acceptance/runs/{runID}/activate [post]
func (s *Server) activateAcceptanceRun(w http.ResponseWriter, r *http.Request) {
	mode, err := s.acceptanceStore().ActivateAcceptanceRun(r.Context(), r.PathValue("runID"))
	if err != nil {
		writeAcceptanceMutationError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, mode)
}

// stageAcceptanceCapacityProfiles godoc
// @Summary 为当前 validation Run 暂存 80% 容量门禁
// @Tags acceptance
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param runID path string true "验收 Run ID"
// @Param body body store.StageAcceptanceCapacityProfilesInput true "待验证容量配置"
// @Success 200 {object} store.AcceptanceRun
// @Router /api/admin/system/acceptance/runs/{runID}/capacity-profiles [post]
func (s *Server) stageAcceptanceCapacityProfiles(w http.ResponseWriter, r *http.Request) {
	var input store.StageAcceptanceCapacityProfilesInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeError(w, http.StatusBadRequest, "invalid json body")
		return
	}
	input.RunID = r.PathValue("runID")
	run, err := s.acceptanceStore().StageAcceptanceCapacityProfiles(r.Context(), input)
	if err != nil {
		switch {
		case store.IsNotFound(err):
			writeError(w, http.StatusConflict, "acceptance run is not running")
		default:
			writeError(w, http.StatusBadRequest, err.Error())
		}
		return
	}
	writeJSON(w, http.StatusOK, run)
}

// finishAcceptanceRun godoc
// @Summary 记录验收门禁结果
// @Tags acceptance
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param runID path string true "验收 Run ID"
// @Param body body store.FinishAcceptanceRunInput true "门禁结果"
// @Success 200 {object} store.AcceptanceRun
// @Router /api/admin/system/acceptance/runs/{runID}/finish [post]
func (s *Server) finishAcceptanceRun(w http.ResponseWriter, r *http.Request) {
	var input store.FinishAcceptanceRunInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeError(w, http.StatusBadRequest, "invalid json body")
		return
	}
	input.RunID = r.PathValue("runID")
	run, err := s.acceptanceStore().FinishAcceptanceRun(r.Context(), input)
	if err != nil {
		if store.IsNotFound(err) {
			writeError(w, http.StatusConflict, "acceptance run is not running")
			return
		}
		writeError(w, http.StatusInternalServerError, "finish acceptance run failed")
		return
	}
	writeJSON(w, http.StatusOK, run)
}

// retryAcceptanceRun godoc
// @Summary 在 validation 关闭正式流量的状态下重试失败 Run
// @Tags acceptance
// @Produce json
// @Security BearerAuth
// @Param runID path string true "验收 Run ID"
// @Success 200 {object} store.AcceptanceRun
// @Router /api/admin/system/acceptance/runs/{runID}/retry [post]
func (s *Server) retryAcceptanceRun(w http.ResponseWriter, r *http.Request) {
	run, err := s.acceptanceStore().RetryAcceptanceRun(r.Context(), r.PathValue("runID"))
	if err != nil {
		writeAcceptanceMutationError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, run)
}

// promoteAcceptanceRun godoc
// @Summary 通过 CAS 切回 live
// @Tags acceptance
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param runID path string true "验收 Run ID"
// @Param body body store.PromoteAcceptanceRunInput true "线上 release 与 digest CAS"
// @Success 200 {object} store.GatewayTrafficMode
// @Router /api/admin/system/acceptance/runs/{runID}/promote [post]
func (s *Server) promoteAcceptanceRun(w http.ResponseWriter, r *http.Request) {
	var input store.PromoteAcceptanceRunInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeError(w, http.StatusBadRequest, "invalid json body")
		return
	}
	input.RunID = r.PathValue("runID")
	mode, err := s.acceptanceStore().PromoteAcceptanceRun(r.Context(), input)
	if err != nil {
		writeAcceptanceMutationError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, mode)
}

// abortAcceptanceRun godoc
// @Summary 人工 CAS 中止验收并切回 live
// @Tags acceptance
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param runID path string true "验收 Run ID"
// @Param body body store.PromoteAcceptanceRunInput true "线上 release 与 digest CAS"
// @Success 200 {object} store.GatewayTrafficMode
// @Router /api/admin/system/acceptance/runs/{runID}/abort [post]
func (s *Server) abortAcceptanceRun(w http.ResponseWriter, r *http.Request) {
	var input store.PromoteAcceptanceRunInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeError(w, http.StatusBadRequest, "invalid json body")
		return
	}
	input.RunID = r.PathValue("runID")
	mode, err := s.acceptanceStore().AbortAcceptanceRun(r.Context(), input)
	if err != nil {
		writeAcceptanceMutationError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, mode)
}

func writeAcceptanceMutationError(w http.ResponseWriter, err error) {
	switch {
	case errors.Is(err, store.ErrAcceptanceStateConflict):
		writeError(w, http.StatusConflict, err.Error())
	case errors.Is(err, store.ErrAcceptancePromotionGates):
		writeError(w, http.StatusPreconditionFailed, err.Error())
	case store.IsNotFound(err):
		writeError(w, http.StatusNotFound, "acceptance run not found")
	default:
		writeError(w, http.StatusInternalServerError, "acceptance state update failed")
	}
}
