package platform

import (
	"encoding/json"
	"net/http"
	"net/url"
	"strings"
	"time"

	"lingniu/vehicle-data-platform/apps/api/internal/httpx"
)

type Handler struct {
	service *Service
	mux     *http.ServeMux
}

func NewHandler(service *Service) *Handler {
	h := &Handler{service: service, mux: http.NewServeMux()}
	h.routes()
	return h
}

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.mux.ServeHTTP(w, r)
}

func (h *Handler) routes() {
	h.mux.HandleFunc("GET /api/dashboard/summary", h.handleDashboardSummary)
	h.mux.HandleFunc("GET /api/vehicles", h.handleVehicles)
	h.mux.HandleFunc("GET /api/vehicles/resolve", h.handleVehicleResolve)
	h.mux.HandleFunc("GET /api/vehicles/coverage", h.handleVehicleCoverage)
	h.mux.HandleFunc("GET /api/vehicles/coverage/summary", h.handleVehicleCoverageSummary)
	h.mux.HandleFunc("GET /api/vehicle-service", h.handleVehicleDetail)
	h.mux.HandleFunc("GET /api/vehicle-service/summary", h.handleVehicleServiceSummary)
	h.mux.HandleFunc("GET /api/vehicle-service/overview", h.handleVehicleServiceOverview)
	h.mux.HandleFunc("POST /api/vehicle-service/overviews", h.handleVehicleServiceOverviews)
	h.mux.HandleFunc("GET /api/vehicles/detail", h.handleVehicleDetail)
	h.mux.HandleFunc("GET /api/realtime/vehicles", h.handleVehicleRealtime)
	h.mux.HandleFunc("GET /api/realtime/locations", h.handleRealtimeLocations)
	h.mux.HandleFunc("GET /api/history/locations", h.handleHistoryLocations)
	h.mux.HandleFunc("GET /api/history/raw-frames", h.handleRawFramesGet)
	h.mux.HandleFunc("POST /api/history/raw-frames/query", h.handleRawFramesPost)
	h.mux.HandleFunc("GET /api/mileage/summary", h.handleMileageSummary)
	h.mux.HandleFunc("GET /api/mileage/daily", h.handleDailyMileage)
	h.mux.HandleFunc("GET /api/v2/statistics/mileage", h.handleMileageStatistics)
	h.mux.HandleFunc("GET /api/statistics/online-summary", h.handleOnlineStatisticsSummary)
	h.mux.HandleFunc("GET /api/statistics/online-vehicles", h.handleOnlineVehicleStatuses)
	h.mux.HandleFunc("GET /api/quality/summary", h.handleQualitySummary)
	h.mux.HandleFunc("GET /api/quality/issues", h.handleQualityIssues)
	h.mux.HandleFunc("GET /api/quality/notification-plan", h.handleQualityNotificationPlan)
	h.mux.HandleFunc("GET /api/alert-events/summary", h.handleQualitySummary)
	h.mux.HandleFunc("GET /api/alert-events", h.handleQualityIssues)
	h.mux.HandleFunc("GET /api/alert-events/notification-plan", h.handleQualityNotificationPlan)
	h.mux.HandleFunc("GET /api/ops/health", h.handleOpsHealth)
	h.mux.HandleFunc("GET /api/ops/source-readiness", h.handleSourceReadiness)
	h.mux.HandleFunc("GET /api/v2/monitor/summary", h.handleMonitorSummary)
	h.mux.HandleFunc("GET /api/v2/monitor/map", h.handleMonitorMap)
	h.mux.HandleFunc("GET /api/v2/monitor/workspace", h.handleMonitorWorkspace)
	h.mux.HandleFunc("GET /api/v2/vehicles/{vin}/profile", h.handleVehicleProfile)
	h.mux.HandleFunc("PUT /api/v2/vehicles/{vin}/profile", h.handleSaveVehicleProfile)
	h.mux.HandleFunc("GET /api/v2/vehicles/{vin}/telemetry/latest", h.handleLatestTelemetry)
	h.mux.HandleFunc("GET /api/v2/vehicles/{vin}/source-evidence", h.handleVehicleSourceEvidence)
	h.mux.HandleFunc("GET /api/v2/operations/vehicles/{vin}/sources", h.handleVehicleSourceDiagnostic)
	h.mux.HandleFunc("PUT /api/v2/operations/vehicles/{vin}/sources/{sourceRef}", h.handleUpdateVehicleSourcePolicy)
	h.mux.HandleFunc("POST /api/v2/vehicle-profiles/sync", h.handleSyncVehicleProfiles)
	h.mux.HandleFunc("GET /api/v2/tracks", h.handleTrackPlayback)
	h.mux.HandleFunc("GET /api/v2/metrics", h.handleMetricCatalog)
	h.mux.HandleFunc("GET /api/v2/history/metrics", h.handleHistoryMetricCatalog)
	h.mux.HandleFunc("GET /api/v2/history/query", h.handleHistoryData)
	h.mux.HandleFunc("GET /api/v2/history/series", h.handleHistorySeries)
	h.mux.HandleFunc("POST /api/v2/exports", h.handleCreateHistoryExport)
	h.mux.HandleFunc("GET /api/v2/exports", h.handleListHistoryExports)
	h.mux.HandleFunc("GET /api/v2/exports/{id}/download", h.handleDownloadHistoryExport)
	h.mux.HandleFunc("POST /api/v2/access/summary", h.handleAccessSummary)
	h.mux.HandleFunc("POST /api/v2/access/vehicles", h.handleAccessVehicles)
	h.mux.HandleFunc("POST /api/v2/access/unresolved-identities", h.handleAccessUnresolvedIdentities)
	h.mux.HandleFunc("GET /api/v2/access/thresholds", h.handleAccessThresholds)
	h.mux.HandleFunc("PUT /api/v2/access/thresholds", h.handleUpdateAccessThresholds)
	h.mux.HandleFunc("POST /api/v2/alerts/summary", h.handleAlertSummary)
	h.mux.HandleFunc("POST /api/v2/alerts/events", h.handleAlertEvents)
	h.mux.HandleFunc("GET /api/v2/alerts/events/{id}", h.handleAlertEvent)
	h.mux.HandleFunc("POST /api/v2/alerts/events/{id}/actions", h.handleAlertAction)
	h.mux.HandleFunc("GET /api/v2/alerts/rules", h.handleAlertRules)
	h.mux.HandleFunc("POST /api/v2/alerts/rules", h.handleSaveAlertRule)
	h.mux.HandleFunc("PUT /api/v2/alerts/rules/{id}", h.handleSaveAlertRule)
	h.mux.HandleFunc("PUT /api/v2/alerts/rules/{id}/enabled", h.handleAlertRuleEnabled)
	h.mux.HandleFunc("GET /api/v2/alerts/notifications", h.handleAlertNotifications)
	h.mux.HandleFunc("POST /api/v2/alerts/notifications/read", h.handleAlertNotificationsRead)
}

func (h *Handler) handleAccessUnresolvedIdentities(w http.ResponseWriter, r *http.Request) {
	var query AccessUnresolvedIdentityQuery
	if !decodeJSONBody(w, r, &query) {
		return
	}
	data, err := h.service.AccessUnresolvedIdentities(r.Context(), query)
	h.write(w, r, data, err)
}

func (h *Handler) handleVehicleProfile(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.VehicleProfile(r.Context(), r.PathValue("vin"))
	h.write(w, r, data, err)
}

func (h *Handler) handleLatestTelemetry(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.LatestTelemetry(r.Context(), r.PathValue("vin"))
	h.write(w, r, data, err)
}

func (h *Handler) handleVehicleSourceEvidence(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.VehicleSourceEvidence(r.Context(), r.PathValue("vin"), r.URL.Query().Get("date"))
	h.write(w, r, data, err)
}

func (h *Handler) handleVehicleSourceDiagnostic(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.VehicleSourceDiagnostic(r.Context(), r.PathValue("vin"))
	h.write(w, r, data, err)
}

func (h *Handler) handleUpdateVehicleSourcePolicy(w http.ResponseWriter, r *http.Request) {
	var update VehicleSourcePolicyUpdate
	if !decodeJSONBody(w, r, &update) {
		return
	}
	update.SourceRef = r.PathValue("sourceRef")
	update.Actor = ActorFromContext(r.Context())
	data, err := h.service.UpdateVehicleSourcePolicy(r.Context(), r.PathValue("vin"), update)
	h.write(w, r, data, err)
}

func (h *Handler) handleSaveVehicleProfile(w http.ResponseWriter, r *http.Request) {
	var input VehicleProfileInput
	if !decodeJSONBody(w, r, &input) {
		return
	}
	input.Actor = ActorFromContext(r.Context())
	data, err := h.service.SaveVehicleProfile(r.Context(), r.PathValue("vin"), input)
	h.write(w, r, data, err)
}

func (h *Handler) handleSyncVehicleProfiles(w http.ResponseWriter, r *http.Request) {
	var request VehicleProfileSyncRequest
	if !decodeJSONBody(w, r, &request) {
		return
	}
	request.Actor = ActorFromContext(r.Context())
	data, err := h.service.SyncVehicleProfiles(r.Context(), request)
	h.write(w, r, data, err)
}

func (h *Handler) handleMetricCatalog(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.MetricCatalog(r.Context())
	h.write(w, r, data, err)
}

func (h *Handler) handleAlertSummary(w http.ResponseWriter, r *http.Request) {
	var query AlertQuery
	if !decodeJSONBody(w, r, &query) {
		return
	}
	data, err := h.service.AlertSummary(r.Context(), query)
	h.write(w, r, data, err)
}

func (h *Handler) handleAlertEvents(w http.ResponseWriter, r *http.Request) {
	var query AlertQuery
	if !decodeJSONBody(w, r, &query) {
		return
	}
	data, err := h.service.AlertEvents(r.Context(), query)
	h.write(w, r, data, err)
}

func (h *Handler) handleAlertEvent(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.AlertEvent(r.Context(), r.PathValue("id"))
	h.write(w, r, data, err)
}

func (h *Handler) handleAlertAction(w http.ResponseWriter, r *http.Request) {
	var request AlertActionRequest
	if !decodeJSONBody(w, r, &request) {
		return
	}
	request.Actor = ActorFromContext(r.Context())
	data, err := h.service.ActOnAlert(r.Context(), r.PathValue("id"), request)
	h.write(w, r, data, err)
}

func (h *Handler) handleAlertRules(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.AlertRules(r.Context())
	h.write(w, r, data, err)
}

func (h *Handler) handleSaveAlertRule(w http.ResponseWriter, r *http.Request) {
	var input AlertRuleInput
	if !decodeJSONBody(w, r, &input) {
		return
	}
	if pathID := r.PathValue("id"); pathID != "" {
		input.ID = pathID
	}
	input.Actor = ActorFromContext(r.Context())
	data, err := h.service.SaveAlertRule(r.Context(), input)
	h.write(w, r, data, err)
}

func (h *Handler) handleAlertRuleEnabled(w http.ResponseWriter, r *http.Request) {
	var update AlertRuleEnabledUpdate
	if !decodeJSONBody(w, r, &update) {
		return
	}
	update.Actor = ActorFromContext(r.Context())
	data, err := h.service.SetAlertRuleEnabled(r.Context(), r.PathValue("id"), update)
	h.write(w, r, data, err)
}

func (h *Handler) handleAlertNotifications(w http.ResponseWriter, r *http.Request) {
	limit := parsePositive(r.URL.Query().Get("limit"), 20)
	offset := parsePositive(r.URL.Query().Get("offset"), 0)
	unread := strings.EqualFold(r.URL.Query().Get("unreadOnly"), "true") || r.URL.Query().Get("unreadOnly") == "1"
	data, err := h.service.AlertNotifications(r.Context(), AlertNotificationQuery{UnreadOnly: unread, Limit: limit, Offset: offset})
	h.write(w, r, data, err)
}

func (h *Handler) handleAlertNotificationsRead(w http.ResponseWriter, r *http.Request) {
	var request AlertNotificationReadRequest
	if !decodeJSONBody(w, r, &request) {
		return
	}
	request.Actor = ActorFromContext(r.Context())
	count, err := h.service.MarkAlertNotificationsRead(r.Context(), request)
	h.write(w, r, map[string]int{"updated": count}, err)
}

func (h *Handler) handleAccessSummary(w http.ResponseWriter, r *http.Request) {
	var query AccessQuery
	if !decodeJSONBody(w, r, &query) {
		return
	}
	data, err := h.service.AccessSummary(r.Context(), query)
	h.write(w, r, data, err)
}

func (h *Handler) handleAccessVehicles(w http.ResponseWriter, r *http.Request) {
	var query AccessQuery
	if !decodeJSONBody(w, r, &query) {
		return
	}
	data, err := h.service.AccessVehicles(r.Context(), query)
	h.write(w, r, data, err)
}

func (h *Handler) handleAccessThresholds(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.AccessThresholds(r.Context())
	h.write(w, r, data, err)
}

func (h *Handler) handleUpdateAccessThresholds(w http.ResponseWriter, r *http.Request) {
	var update AccessThresholdUpdate
	if !decodeJSONBody(w, r, &update) {
		return
	}
	update.Actor = ActorFromContext(r.Context())
	data, err := h.service.UpdateAccessThresholds(r.Context(), update)
	h.write(w, r, data, err)
}

func decodeJSONBody(w http.ResponseWriter, r *http.Request, target any) bool {
	defer r.Body.Close()
	decoder := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1<<20))
	decoder.DisallowUnknownFields()
	if err := decoder.Decode(target); err != nil {
		httpx.WriteError(w, http.StatusBadRequest, "BAD_JSON", "请求 JSON 解析失败", err.Error(), traceID(r))
		return false
	}
	return true
}

func (h *Handler) handleCreateHistoryExport(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	var request HistoryExportRequest
	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
		httpx.WriteError(w, http.StatusBadRequest, "BAD_JSON", "请求 JSON 解析失败", err.Error(), traceID(r))
		return
	}
	data, err := h.service.CreateHistoryExport(r.Context(), request)
	h.write(w, r, data, err)
}

func (h *Handler) handleListHistoryExports(w http.ResponseWriter, r *http.Request) {
	h.write(w, r, h.service.ListHistoryExports(r.Context()), nil)
}

func (h *Handler) handleDownloadHistoryExport(w http.ResponseWriter, r *http.Request) {
	path, name, err := h.service.HistoryExportFile(r.Context(), r.PathValue("id"))
	if err != nil {
		h.write(w, r, nil, err)
		return
	}
	w.Header().Set("Content-Type", "text/csv; charset=utf-8")
	w.Header().Set("Content-Disposition", `attachment; filename="history-export.csv"; filename*=UTF-8''`+url.PathEscape(name))
	w.Header().Set("X-Export-Name", url.PathEscape(name))
	http.ServeFile(w, r, path)
}

func (h *Handler) handleHistoryMetricCatalog(w http.ResponseWriter, r *http.Request) {
	h.write(w, r, h.service.HistoryMetricCatalog(), nil)
}

func (h *Handler) handleHistoryData(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.HistoryData(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleHistorySeries(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.HistorySeries(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleTrackPlayback(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.TrackPlayback(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleMonitorSummary(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.MonitorSummary(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleMonitorMap(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.MonitorMap(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleMonitorWorkspace(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.MonitorWorkspace(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleDashboardSummary(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.DashboardSummary(r.Context())
	h.write(w, r, data, err)
}

func (h *Handler) handleVehicles(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.Vehicles(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleVehicleResolve(w http.ResponseWriter, r *http.Request) {
	keyword := firstNonEmpty(r.URL.Query().Get("keyword"), r.URL.Query().Get("vin"))
	keyword = strings.TrimSpace(keyword)
	if keyword == "" {
		httpx.WriteError(w, http.StatusBadRequest, "VEHICLE_KEY_REQUIRED", "车辆关键词不能为空", "", traceID(r))
		return
	}
	data, err := h.service.ResolveVehicleIdentity(r.Context(), keyword, r.URL.Query().Get("protocol"))
	h.write(w, r, data, err)
}

func (h *Handler) handleVehicleCoverage(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.VehicleCoverage(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleVehicleCoverageSummary(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.VehicleCoverageSummary(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleVehicleServiceSummary(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.VehicleServiceSummary(r.Context())
	h.write(w, r, data, err)
}

func (h *Handler) handleVehicleDetail(w http.ResponseWriter, r *http.Request) {
	keyword := firstNonEmpty(r.URL.Query().Get("keyword"), r.URL.Query().Get("vin"))
	keyword = strings.TrimSpace(keyword)
	if keyword == "" {
		httpx.WriteError(w, http.StatusBadRequest, "VEHICLE_KEY_REQUIRED", "车辆关键词不能为空", "", traceID(r))
		return
	}
	data, err := h.service.VehicleDetail(r.Context(), keyword, r.URL.Query().Get("protocol"))
	h.write(w, r, data, err)
}

func (h *Handler) handleVehicleServiceOverview(w http.ResponseWriter, r *http.Request) {
	keyword := firstNonEmpty(r.URL.Query().Get("keyword"), r.URL.Query().Get("vin"))
	keyword = strings.TrimSpace(keyword)
	if keyword == "" {
		httpx.WriteError(w, http.StatusBadRequest, "VEHICLE_KEY_REQUIRED", "车辆关键词不能为空", "", traceID(r))
		return
	}
	data, err := h.service.VehicleServiceOverview(r.Context(), keyword, r.URL.Query().Get("protocol"))
	h.write(w, r, data, err)
}

func (h *Handler) handleVehicleServiceOverviews(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	var query VehicleOverviewBatchQuery
	if err := json.NewDecoder(r.Body).Decode(&query); err != nil {
		httpx.WriteError(w, http.StatusBadRequest, "BAD_JSON", "请求 JSON 解析失败", err.Error(), traceID(r))
		return
	}
	data, err := h.service.VehicleServiceOverviews(r.Context(), query)
	h.write(w, r, data, err)
}

func (h *Handler) handleVehicleRealtime(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.VehicleRealtime(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleRealtimeLocations(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.RealtimeLocations(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleHistoryLocations(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.HistoryLocations(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleRawFramesGet(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()
	query := RawFrameQuery{
		Protocol:      q.Get("protocol"),
		VIN:           firstNonEmpty(q.Get("vin"), q.Get("keyword")),
		Keyword:       q.Get("keyword"),
		DateFrom:      q.Get("dateFrom"),
		DateTo:        q.Get("dateTo"),
		Fields:        splitCSV(q.Get("fields")),
		IncludeFields: q.Get("includeFields") == "true",
		Limit:         parsePositive(q.Get("limit"), 100),
		Offset:        parsePositive(q.Get("offset"), 0),
	}
	data, err := h.service.RawFrames(r.Context(), query)
	h.write(w, r, data, err)
}

func (h *Handler) handleRawFramesPost(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	var query RawFrameQuery
	if err := json.NewDecoder(r.Body).Decode(&query); err != nil {
		httpx.WriteError(w, http.StatusBadRequest, "BAD_JSON", "请求 JSON 解析失败", err.Error(), traceID(r))
		return
	}
	data, err := h.service.RawFrames(r.Context(), query)
	h.write(w, r, data, err)
}

func (h *Handler) handleDailyMileage(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.DailyMileage(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleMileageSummary(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.MileageSummary(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleMileageStatistics(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.MileageStatistics(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleOnlineStatisticsSummary(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.OnlineStatisticsSummary(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleOnlineVehicleStatuses(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.OnlineVehicleStatuses(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleQualityIssues(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.QualityIssues(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleQualitySummary(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.QualitySummary(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleQualityNotificationPlan(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.QualityNotificationPlan(r.Context(), r.URL.Query())
	h.write(w, r, data, err)
}

func (h *Handler) handleOpsHealth(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.OpsHealth(r.Context())
	h.write(w, r, data, err)
}

func (h *Handler) handleSourceReadiness(w http.ResponseWriter, r *http.Request) {
	data, err := h.service.SourceReadiness(r.Context())
	h.write(w, r, data, err)
}

func (h *Handler) write(w http.ResponseWriter, r *http.Request, data any, err error) {
	if err != nil {
		if clientErr, ok := asClientError(err); ok {
			status := http.StatusBadRequest
			switch {
			case clientErr.Code == "VEHICLE_PERMISSION_DENIED":
				status = http.StatusForbidden
			case strings.HasPrefix(clientErr.Code, "HISTORY_SCOPE_"), clientErr.Code == "HISTORY_BEFORE_AUTHORIZATION":
				status = http.StatusForbidden
			case strings.HasSuffix(clientErr.Code, "_NOT_FOUND"):
				status = http.StatusNotFound
			case strings.HasSuffix(clientErr.Code, "_CONFLICT"), clientErr.Code == "ALERT_ACTION_NOT_ALLOWED":
				status = http.StatusConflict
			}
			httpx.WriteError(w, status, clientErr.Code, clientErr.Message, "", traceID(r))
			return
		}
		httpx.WriteError(w, http.StatusInternalServerError, "INTERNAL", "服务处理失败", err.Error(), traceID(r))
		return
	}
	httpx.WriteOK(w, traceID(r), data)
}

func traceID(r *http.Request) string {
	if value := r.Header.Get("X-Trace-Id"); value != "" {
		return value
	}
	return "trace-" + time.Now().Format("20060102150405.000000")
}

func splitCSV(value string) []string {
	if strings.TrimSpace(value) == "" {
		return []string{}
	}
	parts := strings.Split(value, ",")
	out := make([]string, 0, len(parts))
	for _, part := range parts {
		if trimmed := strings.TrimSpace(part); trimmed != "" {
			out = append(out, trimmed)
		}
	}
	return out
}
