// Copyright 2026 printing-press-golden and contributors. Licensed under Apache-2.0. See LICENSE.
// Generated by CLI Printing Press (https://github.com/mvanhorn/cli-printing-press). DO NOT EDIT.

package bound

import (
	"encoding/json"
	"strings"
	"testing"
	"unicode/utf8"
)

func TestEndpointResponseBoundsListResponses(t *testing.T) {
	items := make([]string, 0, MaxItems+25)
	for i := 0; i < MaxItems+25; i++ {
		items = append(items, strings.Repeat("x", 1600))
	}
	data, err := json.Marshal(items)
	if err != nil {
		t.Fatalf("marshal fixture: %v", err)
	}

	text := EndpointResponse("GET", data)
	if len(text) > MaxBytes {
		t.Fatalf("bounded result length = %d, want <= %d", len(text), MaxBytes)
	}

	var envelope struct {
		Count         int               `json:"count"`
		Items         []json.RawMessage `json:"items"`
		Truncated     bool              `json:"truncated"`
		ReturnedCount int               `json:"returned_count"`
		OriginalBytes int               `json:"original_bytes"`
	}
	if err := json.Unmarshal([]byte(text), &envelope); err != nil {
		t.Fatalf("bounded list result must remain valid JSON: %v\n%s", err, text)
	}
	if !envelope.Truncated {
		t.Fatalf("bounded list result did not mark truncation: %s", text)
	}
	if envelope.Count != len(items) {
		t.Fatalf("count = %d, want %d", envelope.Count, len(items))
	}
	if envelope.ReturnedCount != len(envelope.Items) {
		t.Fatalf("returned_count = %d, want item count %d", envelope.ReturnedCount, len(envelope.Items))
	}
	if envelope.OriginalBytes != len(data) {
		t.Fatalf("original_bytes = %d, want %d", envelope.OriginalBytes, len(data))
	}
}

func TestEndpointResponseTruncatedByItemLimitDoesNotClaimByteOverflow(t *testing.T) {
	items := make([]string, 0, MaxItems+1)
	for i := 0; i < MaxItems+1; i++ {
		items = append(items, "ok")
	}
	data, err := json.Marshal(items)
	if err != nil {
		t.Fatalf("marshal fixture: %v", err)
	}
	if len(data) > MaxBytes {
		t.Fatalf("fixture should fit byte budget: %d > %d", len(data), MaxBytes)
	}

	text := EndpointResponse("GET", data)
	var envelope struct {
		Truncated     bool   `json:"truncated"`
		ReturnedCount int    `json:"returned_count"`
		OriginalBytes int    `json:"original_bytes"`
		MaxBytes      int    `json:"max_bytes"`
		Note          string `json:"note"`
	}
	if err := json.Unmarshal([]byte(text), &envelope); err != nil {
		t.Fatalf("bounded list result must remain valid JSON: %v\n%s", err, text)
	}
	if !envelope.Truncated {
		t.Fatalf("bounded list result did not mark truncation: %s", text)
	}
	if envelope.ReturnedCount != MaxItems {
		t.Fatalf("returned_count = %d, want %d", envelope.ReturnedCount, MaxItems)
	}
	if envelope.OriginalBytes > envelope.MaxBytes {
		t.Fatalf("fixture should be item-limited, not byte-limited: original=%d max=%d", envelope.OriginalBytes, envelope.MaxBytes)
	}
	if strings.Contains(envelope.Note, "exceeded") {
		t.Fatalf("item-limited note should not claim byte overflow: %q", envelope.Note)
	}
}

func TestEndpointResponseBoundsSingleArrayEnvelope(t *testing.T) {
	groups := make([]map[string]string, 0, MaxItems+25)
	for i := 0; i < MaxItems+25; i++ {
		groups = append(groups, map[string]string{
			"id":   strings.Repeat("g", 8),
			"name": strings.Repeat("verbose group name ", 90),
		})
	}
	data, err := json.Marshal(map[string]any{
		"groups": groups,
		"cursor": "next-page",
	})
	if err != nil {
		t.Fatalf("marshal fixture: %v", err)
	}

	text := EndpointResponse("GET", data)
	if len(text) > MaxBytes {
		t.Fatalf("bounded result length = %d, want <= %d", len(text), MaxBytes)
	}

	var envelope struct {
		Groups        []json.RawMessage `json:"groups"`
		Cursor        string            `json:"cursor"`
		Truncated     bool              `json:"_pp_truncated"`
		TotalCount    int               `json:"_pp_total_count"`
		ReturnedCount int               `json:"_pp_returned_count"`
	}
	if err := json.Unmarshal([]byte(text), &envelope); err != nil {
		t.Fatalf("bounded envelope result must remain valid JSON: %v\n%s", err, text)
	}
	if !envelope.Truncated {
		t.Fatalf("bounded envelope result did not mark truncation: %s", text)
	}
	if envelope.Cursor != "next-page" {
		t.Fatalf("cursor = %q, want preserved metadata", envelope.Cursor)
	}
	if envelope.TotalCount != len(groups) {
		t.Fatalf("total_count = %d, want %d", envelope.TotalCount, len(groups))
	}
	if envelope.ReturnedCount != len(envelope.Groups) {
		t.Fatalf("returned_count = %d, want group count %d", envelope.ReturnedCount, len(envelope.Groups))
	}
}

func TestEndpointResponseFallsBackToOversizedPreview(t *testing.T) {
	data, err := json.Marshal(map[string]string{
		"blob": strings.Repeat("z", MaxBytes+10000),
	})
	if err != nil {
		t.Fatalf("marshal fixture: %v", err)
	}

	text := EndpointResponse("GET", data)
	if len(text) > MaxBytes {
		t.Fatalf("preview result length = %d, want <= %d", len(text), MaxBytes)
	}

	var envelope struct {
		Truncated     bool   `json:"truncated"`
		OriginalBytes int    `json:"original_bytes"`
		Preview       string `json:"preview"`
	}
	if err := json.Unmarshal([]byte(text), &envelope); err != nil {
		t.Fatalf("preview result must remain valid JSON: %v\n%s", err, text)
	}
	if !envelope.Truncated {
		t.Fatalf("preview result did not mark truncation: %s", text)
	}
	if envelope.OriginalBytes != len(data) {
		t.Fatalf("original_bytes = %d, want %d", envelope.OriginalBytes, len(data))
	}
	if envelope.Preview == "" {
		t.Fatalf("preview result should include a bounded preview")
	}
}

func TestEndpointResponseBoundsOversizedNonGETResponses(t *testing.T) {
	data, err := json.Marshal(map[string]string{
		"blob": strings.Repeat("z", MaxBytes+10000),
	})
	if err != nil {
		t.Fatalf("marshal fixture: %v", err)
	}

	text := EndpointResponse("POST", data)
	if len(text) > MaxBytes {
		t.Fatalf("preview result length = %d, want <= %d", len(text), MaxBytes)
	}

	var envelope struct {
		Truncated     bool   `json:"truncated"`
		OriginalBytes int    `json:"original_bytes"`
		Preview       string `json:"preview"`
	}
	if err := json.Unmarshal([]byte(text), &envelope); err != nil {
		t.Fatalf("preview result must remain valid JSON: %v\n%s", err, text)
	}
	if !envelope.Truncated {
		t.Fatalf("preview result did not mark truncation: %s", text)
	}
	if envelope.OriginalBytes != len(data) {
		t.Fatalf("original_bytes = %d, want %d", envelope.OriginalBytes, len(data))
	}
	if envelope.Preview == "" {
		t.Fatalf("preview result should include a bounded preview")
	}
}

func TestJSONBoundsLargeArrays(t *testing.T) {
	rows := make([]map[string]string, 0, MaxItems+30)
	for i := 0; i < MaxItems+30; i++ {
		rows = append(rows, map[string]string{
			"id":   strings.Repeat("id", 80),
			"text": strings.Repeat("search result payload ", 90),
		})
	}

	text, err := JSON(rows)
	if err != nil {
		t.Fatalf("JSON returned error: %v", err)
	}
	if len(text) > MaxBytes {
		t.Fatalf("bounded JSON length = %d, want <= %d", len(text), MaxBytes)
	}

	var envelope struct {
		Count         int               `json:"count"`
		Items         []json.RawMessage `json:"items"`
		Truncated     bool              `json:"truncated"`
		ReturnedCount int               `json:"returned_count"`
		OriginalBytes int               `json:"original_bytes"`
	}
	if err := json.Unmarshal([]byte(text), &envelope); err != nil {
		t.Fatalf("bounded JSON result must remain valid JSON: %v\n%s", err, text)
	}
	if !envelope.Truncated {
		t.Fatalf("bounded JSON result did not mark truncation: %s", text)
	}
	if envelope.Count != len(rows) {
		t.Fatalf("count = %d, want %d", envelope.Count, len(rows))
	}
	if envelope.ReturnedCount != len(envelope.Items) {
		t.Fatalf("returned_count = %d, want item count %d", envelope.ReturnedCount, len(envelope.Items))
	}
	if envelope.OriginalBytes == 0 {
		t.Fatalf("original_bytes should be populated")
	}
}

func TestJSONReturnsCompactFullArrayWhenIndentedOutputExceedsBudget(t *testing.T) {
	rows := make([]map[string]string, 11000)

	text, err := JSON(rows)
	if err != nil {
		t.Fatalf("JSON returned error: %v", err)
	}
	if len(text) > MaxBytes {
		t.Fatalf("compact JSON length = %d, want <= %d", len(text), MaxBytes)
	}
	if strings.Contains(text, `"truncated"`) {
		t.Fatalf("compact full result should not be marked truncated: %s", text[:200])
	}

	var out []map[string]string
	if err := json.Unmarshal([]byte(text), &out); err != nil {
		t.Fatalf("compact full result must remain an array: %v\n%s", err, text[:200])
	}
	if len(out) != len(rows) {
		t.Fatalf("returned rows = %d, want %d", len(out), len(rows))
	}
}

func TestTextBoundsLargeCommandOutput(t *testing.T) {
	text := Text(strings.Repeat("plain output line\n", 5000))
	if len(text) > MaxBytes {
		t.Fatalf("bounded text length = %d, want <= %d", len(text), MaxBytes)
	}

	var envelope struct {
		Truncated     bool   `json:"truncated"`
		OriginalBytes int    `json:"original_bytes"`
		Preview       string `json:"preview"`
	}
	if err := json.Unmarshal([]byte(text), &envelope); err != nil {
		t.Fatalf("bounded text result must be a JSON envelope: %v\n%s", err, text)
	}
	if !envelope.Truncated {
		t.Fatalf("bounded text result did not mark truncation: %s", text)
	}
	if envelope.OriginalBytes == 0 {
		t.Fatalf("original_bytes should be populated")
	}
	if envelope.Preview == "" {
		t.Fatalf("preview should be populated")
	}
}

func TestTextPreviewPreservesUTF8RuneBoundaries(t *testing.T) {
	text := Text(strings.Repeat("€", MaxBytes))

	var envelope struct {
		Preview string `json:"preview"`
	}
	if err := json.Unmarshal([]byte(text), &envelope); err != nil {
		t.Fatalf("bounded text result must be a JSON envelope: %v\n%s", err, text)
	}
	if envelope.Preview == "" {
		t.Fatalf("preview should be populated")
	}
	if !utf8.ValidString(envelope.Preview) {
		t.Fatalf("preview should remain valid UTF-8")
	}
	if strings.ContainsRune(envelope.Preview, utf8.RuneError) {
		t.Fatalf("preview introduced a replacement character at a split UTF-8 boundary: %q", envelope.Preview[len(envelope.Preview)-12:])
	}
}
