package httpapi

import (
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"net/http"
	"strings"

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

// listBillingSettlements godoc
// @Summary 查询计费结算队列
// @Description 管理端分页查询待结算、重试失败和人工复核记录。
// @Tags billing
// @Produce json
// @Security BearerAuth
// @Param status query string false "结算状态"
// @Param action query string false "动作：settle 或 release"
// @Param page query int false "页码" default(1)
// @Param pageSize query int false "每页数量" default(50)
// @Success 200 {object} BillingSettlementListResponse
// @Failure 400 {object} ErrorEnvelope
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/runtime/billing-settlements [get]
func (s *Server) listBillingSettlements(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query()
	page, err := positiveQueryInt(query.Get("page"), 1)
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid page", "invalid_request")
		return
	}
	pageSize, err := positiveQueryInt(query.Get("pageSize"), 50)
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid pageSize", "invalid_request")
		return
	}
	result, err := s.store.ListBillingSettlements(r.Context(), store.BillingSettlementListFilter{
		Status: query.Get("status"), Action: query.Get("action"), Page: page, PageSize: pageSize,
	})
	if err != nil {
		s.logger.Error("list billing settlements failed", "error_category", "billing_settlement_list_failed")
		writeError(w, http.StatusInternalServerError, "list billing settlements failed", "billing_settlement_list_failed")
		return
	}
	writeJSON(w, http.StatusOK, result)
}

// retryBillingSettlement godoc
// @Summary 重试计费结算
// @Description Manager 使用单值 Idempotency-Key 将重试失败或人工复核记录重新放入队列，并记录审计日志。
// @Tags billing
// @Produce json
// @Security BearerAuth
// @Param settlementId path string true "结算记录 ID"
// @Param Idempotency-Key header string true "幂等键"
// @Success 200 {object} BillingSettlementRetryResponse
// @Failure 400 {object} ErrorEnvelope
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 404 {object} ErrorEnvelope
// @Failure 409 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/runtime/billing-settlements/{settlementId}/retry [post]
func (s *Server) retryBillingSettlement(w http.ResponseWriter, r *http.Request) {
	key, ok := singleIdempotencyKey(r)
	if !ok {
		writeError(w, http.StatusBadRequest, "a single Idempotency-Key is required", "idempotency_key_required")
		return
	}
	keyHash := sha256.Sum256([]byte(key))
	settlementID := strings.TrimSpace(r.PathValue("settlementId"))
	if _, err := uuid.Parse(settlementID); err != nil {
		writeError(w, http.StatusBadRequest, "invalid billing settlement ID", "invalid_request")
		return
	}
	actor, _ := auth.UserFromContext(r.Context())
	var item store.BillingSettlement
	var audit store.AuditLog
	var replayed bool
	err := s.store.InTx(r.Context(), func(tx store.Tx) error {
		var err error
		item, replayed, err = s.store.RetryBillingSettlementTx(r.Context(), tx, settlementID, hex.EncodeToString(keyHash[:]))
		if err != nil || replayed {
			return err
		}
		audit, err = s.store.RecordAuditLogTx(r.Context(), tx, billingSettlementRetryAuditInput(r, actor, item))
		return err
	})
	if err != nil {
		switch {
		case store.IsNotFound(err):
			writeError(w, http.StatusNotFound, "billing settlement not found", "billing_settlement_not_found")
		case errors.Is(err, store.ErrBillingSettlementNotRetryable):
			writeError(w, http.StatusConflict, "billing settlement is not retryable", "billing_settlement_not_retryable")
		default:
			s.logger.Error("retry billing settlement failed", "settlementID", settlementID, "error_category", "billing_settlement_retry_failed")
			writeError(w, http.StatusInternalServerError, "retry billing settlement failed", "billing_settlement_retry_failed")
		}
		return
	}
	if replayed {
		w.Header().Set("Idempotent-Replayed", "true")
	}
	response := map[string]any{"settlement": item}
	if !replayed {
		response["auditLog"] = audit
	}
	writeJSON(w, http.StatusOK, response)
}

func singleIdempotencyKey(r *http.Request) (string, bool) {
	values := r.Header.Values("Idempotency-Key")
	if len(values) != 1 {
		return "", false
	}
	value := strings.TrimSpace(values[0])
	if value == "" || len(value) > 255 || strings.Contains(value, ",") {
		return "", false
	}
	return value, true
}

func billingSettlementRetryAuditInput(r *http.Request, actor *auth.User, item store.BillingSettlement) store.AuditLogInput {
	input := store.AuditLogInput{
		Category: "billing", Action: "billing.settlement.retry",
		TargetType: "billing_settlement", TargetID: item.ID,
		RequestIP: requestIP(r), UserAgent: r.UserAgent(),
		AfterState: map[string]any{"status": item.Status, "attempts": item.Attempts},
		Metadata:   map[string]any{"taskId": item.TaskID, "action": item.Action, "currency": item.Currency},
	}
	if actor != nil {
		input.ActorGatewayUserID = uuidText(firstNonEmptyText(actor.GatewayUserID, actor.ID))
		input.ActorUserID = actor.ID
		input.ActorUsername = actor.Username
		input.ActorSource = actor.Source
		input.ActorRoles = actor.Roles
	}
	return input
}
