package pipeline

import (
	"encoding/json"
	"strings"
	"testing"

	"gitea.maximumdirect.net/eric/notarius/internal/core/source"
)

func TestChunkCanonicalizationAndClonePreserveAnnotationScopes(t *testing.T) {
	doc := validSourceDocument()
	plan := source.ChunkPlan{
		SourceDigest: doc.Digest,
		Ranges: []source.ChunkRange{{
			StartUnitID: doc.Units[0].ID, EndUnitID: doc.Units[0].ID,
			Annotations: source.ChunkAnnotations{"shared": json.RawMessage(` {"range": true} `)},
		}},
		Annotations: source.ChunkAnnotations{"shared": json.RawMessage(` {"plan": true} `)},
	}
	canonical, chunks, err := validateAndMaterializeChunkPlan(doc, plan)
	if err != nil {
		t.Fatalf("validateAndMaterializeChunkPlan() error = %v", err)
	}
	if got := string(canonical.Ranges[0].Annotations["shared"]); got != `{"range":true}` {
		t.Fatalf("range annotation = %q", got)
	}
	if got := string(canonical.Annotations["shared"]); got != `{"plan":true}` {
		t.Fatalf("plan annotation = %q", got)
	}

	cloned, err := cloneSourceChunk(chunks[0])
	if err != nil {
		t.Fatal(err)
	}
	cloned.Annotations["shared"][0] = '['
	cloned.PlanAnnotations["shared"][0] = '['
	if string(chunks[0].Annotations["shared"]) != `{"range":true}` || string(chunks[0].PlanAnnotations["shared"]) != `{"plan":true}` {
		t.Fatal("cloneSourceChunk() shares annotation bytes")
	}
}

func TestSerializedChunkValidationRepresentationIncludesAnnotationScopes(t *testing.T) {
	doc := validSourceDocument()
	chunks := []source.Chunk{{
		ID: "chunk-1", SourceID: doc.ID, Ref: doc.Units[0].Ref, MediaType: "application/json", Units: doc.Units[:1],
		Annotations:     source.ChunkAnnotations{"same": json.RawMessage(`{"range":1}`)},
		PlanAnnotations: source.ChunkAnnotations{"same": json.RawMessage(`{"plan":2}`)},
	}}
	encoded, err := json.Marshal(chunks)
	if err != nil {
		t.Fatalf("json.Marshal(chunks) error = %v", err)
	}
	got := string(encoded)
	if !strings.Contains(got, `"annotations":{"same":{"range":1}}`) || !strings.Contains(got, `"plan_annotations":{"same":{"plan":2}}`) {
		t.Fatalf("serialized chunks = %s, want both annotation scopes", got)
	}
}

func TestDebugChunkEnvelopeClonesAnnotationScopes(t *testing.T) {
	chunk := source.Chunk{
		ID: "chunk-1", SourceID: "source-1",
		Annotations:     source.ChunkAnnotations{"same": json.RawMessage(`{"range":1}`)},
		PlanAnnotations: source.ChunkAnnotations{"same": json.RawMessage(`{"plan":2}`)},
	}
	envelope := debugSourceChunkEnvelope(chunk)
	if string(envelope.Annotations["same"]) != `{"range":1}` || string(envelope.PlanAnnotations["same"]) != `{"plan":2}` {
		t.Fatalf("debug annotations = %#v / %#v", envelope.Annotations, envelope.PlanAnnotations)
	}
	envelope.Annotations["same"][0] = '['
	envelope.PlanAnnotations["same"][0] = '['
	if string(chunk.Annotations["same"]) != `{"range":1}` || string(chunk.PlanAnnotations["same"]) != `{"plan":2}` {
		t.Fatal("debugSourceChunkEnvelope() shares annotation bytes")
	}
}
