package pipeline

import (
	"fmt"
	"strings"

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

// debugEvidenceContextSummary intentionally contains only publication-safe
// identifiers and aggregate counts. The evidence document itself can include
// source text and must never be written to this debug envelope.
type debugEvidenceContextSummary struct {
	ArtifactKind  contracts.ArtifactKind `json:"artifact_kind"`
	MediaType     string                 `json:"media_type"`
	SchemaID      string                 `json:"schema_id"`
	SchemaName    string                 `json:"schema_name"`
	SchemaVersion string                 `json:"schema_version"`
	SelectedLanes []string               `json:"selected_lanes"`
	WindowUnits   int                    `json:"window_units"`
	UnitCount     int                    `json:"unit_count"`
	SourceDigest  string                 `json:"source_digest"`
}

// buildOutputEvidenceContext projects the prepared output policy from accepted
// normalized artifacts. It is intentionally separate from lane execution so
// checkpointed normalized outputs use the same reconstruction path.
func buildOutputEvidenceContext(prepared *PreparedPipeline, doc *source.SourceDocument, outputs []contracts.SerializedOutput) (*contracts.SerializedArtifact, *debugEvidenceContextSummary, error) {
	if prepared == nil || prepared.evidencePlan == nil {
		return nil, nil, nil
	}
	if doc == nil {
		return nil, nil, fmt.Errorf("evidence context output: source document is unavailable")
	}
	if prepared.artifactCodecs == nil {
		return nil, nil, fmt.Errorf("evidence context output: artifact codecs are unavailable")
	}

	byLane := make(map[string]contracts.SerializedOutput, len(outputs))
	for _, output := range outputs {
		laneID := strings.TrimSpace(output.LaneID)
		if _, exists := byLane[laneID]; exists {
			return nil, nil, fmt.Errorf("evidence context output: accepted normalized outputs contain duplicate lane %q", laneID)
		}
		byLane[laneID] = contracts.CloneSerializedOutput(output)
	}

	request := evidencecontext.BuildRequest{
		Source:      doc,
		WindowUnits: prepared.evidencePlan.policy.WindowUnits,
		SourceRefs:  make([]source.SourceRef, 0),
	}
	for _, lane := range prepared.evidencePlan.lanes {
		output, ok := byLane[lane.laneID]
		if !ok {
			continue
		}
		if output.SourceID != doc.ID {
			return nil, nil, fmt.Errorf("evidence context output lane %q: accepted normalized output source is incompatible", lane.laneID)
		}
		if output.Artifact.Kind != lane.kind {
			return nil, nil, fmt.Errorf("evidence context output lane %q: accepted normalized output artifact kind is incompatible", lane.laneID)
		}
		value, err := prepared.artifactCodecs.Decode(contracts.CloneSerializedArtifact(output.Artifact))
		if err != nil {
			return nil, nil, fmt.Errorf("evidence context output lane %q: accepted normalized artifact cannot be decoded", lane.laneID)
		}
		references, err := lane.project(value)
		if err != nil {
			return nil, nil, fmt.Errorf("evidence context output lane %q: accepted normalized artifact cannot be projected", lane.laneID)
		}
		request.SourceRefs = append(request.SourceRefs, references...)
	}

	document, err := evidencecontext.Build(request)
	if err != nil {
		return nil, nil, fmt.Errorf("evidence context output: accepted evidence references are invalid")
	}
	content, err := evidencecontext.New().Encode(document)
	if err != nil {
		return nil, nil, fmt.Errorf("evidence context output: evidence context serialization failed")
	}
	artifact := &contracts.SerializedArtifact{
		Kind:      evidencecontext.ArtifactKind,
		Schema:    evidencecontext.New().Schema(),
		MediaType: evidencecontext.MediaType,
		Content:   content,
	}
	summary := debugEvidenceContextSummary{
		ArtifactKind:  artifact.Kind,
		MediaType:     artifact.MediaType,
		SchemaID:      artifact.Schema.ID,
		SchemaName:    artifact.Schema.Name,
		SchemaVersion: artifact.Schema.Version,
		SelectedLanes: append([]string(nil), prepared.evidencePlan.policy.LaneIDs...),
		WindowUnits:   prepared.evidencePlan.policy.WindowUnits,
		SourceDigest:  doc.Digest,
		UnitCount:     len(document),
	}
	return contracts.CloneSerializedArtifactPointer(artifact), &summary, nil
}
