// Package apptracking defines the transport and persistence limits shared by the
// public App tracking endpoint and statistics-service.
package apptracking

import (
	"bytes"
	"encoding/json"
	"strings"
)

const (
	// MaxBatchSize keeps one request bounded before gateway fans it into the
	// statistics write transaction.
	MaxBatchSize = 50
	// MaxPropertiesBytes bounds the raw JSON value after surrounding whitespace
	// is removed. The value is rejected instead of truncated because truncation
	// can change JSON meaning or produce invalid JSON.
	MaxPropertiesBytes = 4096

	// Text limits are byte limits, not rune limits. They mirror the durable
	// app_tracking_events columns and therefore apply identically at both service
	// boundaries; an oversized client value must never be deferred to MySQL.
	MaxAppCodeBytes    = 32
	MaxEventIDBytes    = 160
	MaxEventNameBytes  = 96
	MaxEventTypeBytes  = 64
	MaxScreenBytes     = 128
	MaxTargetTypeBytes = 64
	MaxTargetIDBytes   = 128
	MaxDeviceIDBytes   = 128
	MaxSessionIDBytes  = 160
	MaxPlatformBytes   = 32
	MaxAppVersionBytes = 64
	MaxLanguageBytes   = 32
	MaxTimezoneBytes   = 64
	MaxErrorCodeBytes  = 64
)

// ValidBatchSize rejects both empty reports and reports large enough to hold a
// statistics transaction open for an unbounded period.
func ValidBatchSize(size int) bool {
	return size > 0 && size <= MaxBatchSize
}

// NormalizeText removes transport-only surrounding whitespace and checks the
// encoded byte length. Oversized values return false and are never shortened:
// identifiers and error codes are facts whose suffix may be significant.
func NormalizeText(value string, maxBytes int) (string, bool) {
	value = strings.TrimSpace(value)
	if maxBytes > 0 && len(value) > maxBytes {
		return "", false
	}
	return value, true
}

// NormalizeProperties returns a detached, trimmed JSON value. Empty and JSON
// null both mean no properties; non-empty values must satisfy the same byte and
// syntax contract in gateway and statistics-service.
func NormalizeProperties(raw json.RawMessage) (json.RawMessage, bool) {
	trimmed := bytes.TrimSpace(raw)
	if len(trimmed) == 0 || bytes.Equal(trimmed, []byte("null")) {
		return nil, true
	}
	if len(trimmed) > MaxPropertiesBytes || !json.Valid(trimmed) {
		return nil, false
	}
	return json.RawMessage(bytes.Clone(trimmed)), true
}
