package catalogsnapshot

import (
	"fmt"
)

// BuildEffectiveMetadata validates effective L1+L2 metadata through the one
// Catalog compiler, then emits the closed canonical input grammar. The source
// revisions collapse losslessly into Lineage.CatalogRevision because mixed
// revisions are rejected before any bytes are produced.
func BuildEffectiveMetadata(input CompileInput) (EffectiveMetadata, error) {
	snapshot, err := CompileCatalogSnapshot(input)
	if err != nil {
		return EffectiveMetadata{}, err
	}
	var doc catalogDocument
	if err := decodeStrict(snapshot.bytes, &doc); err != nil {
		return EffectiveMetadata{}, conflict(ConflictEncoding, "effectiveMetadata", "decode compiler output: %v", err)
	}
	if doc.Schema == CatalogSnapshotSchemaV21 {
		doc.Schema = EffectiveMetadataSchemaV20
	} else if doc.Schema == CatalogSnapshotSchemaV20 {
		doc.Schema = EffectiveMetadataSchemaV19
	} else if doc.Schema == CatalogSnapshotSchemaV19 {
		doc.Schema = EffectiveMetadataSchemaV18
	} else if doc.Schema == CatalogSnapshotSchemaV18 {
		doc.Schema = EffectiveMetadataSchemaV17
	} else if doc.Schema == CatalogSnapshotSchemaV17 {
		doc.Schema = EffectiveMetadataSchemaV16
	} else if doc.Schema == CatalogSnapshotSchemaV16 {
		doc.Schema = EffectiveMetadataSchemaV15
	} else if doc.Schema == CatalogSnapshotSchemaV15 {
		doc.Schema = EffectiveMetadataSchemaV14
	} else if doc.Schema == CatalogSnapshotSchemaV14 {
		doc.Schema = EffectiveMetadataSchemaV13
	} else if doc.Schema == CatalogSnapshotSchemaV13 {
		doc.Schema = EffectiveMetadataSchemaV12
	} else if doc.Schema == CatalogSnapshotSchemaV12 {
		doc.Schema = EffectiveMetadataSchemaV11
	} else if doc.Schema == CatalogSnapshotSchemaV11 {
		doc.Schema = EffectiveMetadataSchemaV10
	} else if doc.Schema == CatalogSnapshotSchemaV10 {
		doc.Schema = EffectiveMetadataSchemaV9
	} else if doc.Schema == CatalogSnapshotSchemaV9 {
		doc.Schema = EffectiveMetadataSchemaV8
	} else if doc.Schema == CatalogSnapshotSchemaV8 {
		doc.Schema = EffectiveMetadataSchemaV7
	} else if doc.Schema == CatalogSnapshotSchemaV6 {
		doc.Schema = EffectiveMetadataSchemaV5
	} else if doc.Schema == CatalogSnapshotSchemaV5 {
		doc.Schema = EffectiveMetadataSchemaV4
	} else if doc.Schema == CatalogSnapshotSchemaV4 {
		doc.Schema = EffectiveMetadataSchemaV3
	} else if doc.Schema == CatalogSnapshotSchemaV3 {
		doc.Schema = EffectiveMetadataSchemaV2
	} else {
		doc.Schema = EffectiveMetadataSchemaV1
	}
	data, err := marshalCanonical(doc)
	if err != nil {
		return EffectiveMetadata{}, conflict(ConflictEncoding, "effectiveMetadata", "encode canonical input: %v", err)
	}
	return EffectiveMetadata{digest: ComputeDigest(data), bytes: data}, nil
}

// OpenEffectiveMetadata verifies an immutable input against its pinned digest,
// rejects unknown/non-canonical JSON, and revalidates every effective fact.
func OpenEffectiveMetadata(expected Digest, data []byte) (EffectiveMetadata, error) {
	if !validDigest(expected) {
		return EffectiveMetadata{}, fmt.Errorf("%w: malformed effective metadata digest %q", ErrArtifactCorrupt, expected)
	}
	if actual := ComputeDigest(data); actual != expected {
		return EffectiveMetadata{}, fmt.Errorf("%w: effective metadata digest mismatch: expected %s, got %s", ErrArtifactCorrupt, expected, actual)
	}
	var doc catalogDocument
	if err := decodeStrict(data, &doc); err != nil {
		return EffectiveMetadata{}, fmt.Errorf("%w: decode effective metadata: %v", ErrArtifactCorrupt, err)
	}
	if doc.Schema != EffectiveMetadataSchemaV1 && doc.Schema != EffectiveMetadataSchemaV2 && doc.Schema != EffectiveMetadataSchemaV3 && doc.Schema != EffectiveMetadataSchemaV4 && doc.Schema != EffectiveMetadataSchemaV5 && doc.Schema != EffectiveMetadataSchemaV7 && doc.Schema != EffectiveMetadataSchemaV8 && doc.Schema != EffectiveMetadataSchemaV9 && doc.Schema != EffectiveMetadataSchemaV10 && doc.Schema != EffectiveMetadataSchemaV11 && doc.Schema != EffectiveMetadataSchemaV12 && doc.Schema != EffectiveMetadataSchemaV13 && doc.Schema != EffectiveMetadataSchemaV14 && doc.Schema != EffectiveMetadataSchemaV15 && doc.Schema != EffectiveMetadataSchemaV16 && doc.Schema != EffectiveMetadataSchemaV17 && doc.Schema != EffectiveMetadataSchemaV18 && doc.Schema != EffectiveMetadataSchemaV19 && doc.Schema != EffectiveMetadataSchemaV20 {
		return EffectiveMetadata{}, fmt.Errorf("%w: effective metadata schema %q", ErrUnsupportedSchema, doc.Schema)
	}
	if err := requireCanonicalDocument(data, doc); err != nil {
		return EffectiveMetadata{}, err
	}
	if doc.Schema == EffectiveMetadataSchemaV20 {
		doc.Schema = CatalogSnapshotSchemaV21
	} else if doc.Schema == EffectiveMetadataSchemaV19 {
		doc.Schema = CatalogSnapshotSchemaV20
	} else if doc.Schema == EffectiveMetadataSchemaV18 {
		doc.Schema = CatalogSnapshotSchemaV19
	} else if doc.Schema == EffectiveMetadataSchemaV17 {
		doc.Schema = CatalogSnapshotSchemaV18
	} else if doc.Schema == EffectiveMetadataSchemaV16 {
		doc.Schema = CatalogSnapshotSchemaV17
	} else if doc.Schema == EffectiveMetadataSchemaV15 {
		doc.Schema = CatalogSnapshotSchemaV16
	} else if doc.Schema == EffectiveMetadataSchemaV14 {
		doc.Schema = CatalogSnapshotSchemaV15
	} else if doc.Schema == EffectiveMetadataSchemaV13 {
		doc.Schema = CatalogSnapshotSchemaV14
	} else if doc.Schema == EffectiveMetadataSchemaV12 {
		doc.Schema = CatalogSnapshotSchemaV13
	} else if doc.Schema == EffectiveMetadataSchemaV11 {
		doc.Schema = CatalogSnapshotSchemaV12
	} else if doc.Schema == EffectiveMetadataSchemaV10 {
		doc.Schema = CatalogSnapshotSchemaV11
	} else if doc.Schema == EffectiveMetadataSchemaV9 {
		doc.Schema = CatalogSnapshotSchemaV10
	} else if doc.Schema == EffectiveMetadataSchemaV8 {
		doc.Schema = CatalogSnapshotSchemaV9
	} else if doc.Schema == EffectiveMetadataSchemaV7 {
		doc.Schema = CatalogSnapshotSchemaV8
	} else if doc.Schema == EffectiveMetadataSchemaV5 {
		doc.Schema = CatalogSnapshotSchemaV6
	} else if doc.Schema == EffectiveMetadataSchemaV4 {
		doc.Schema = CatalogSnapshotSchemaV5
	} else if doc.Schema == EffectiveMetadataSchemaV3 {
		doc.Schema = CatalogSnapshotSchemaV4
	} else if doc.Schema == EffectiveMetadataSchemaV2 {
		doc.Schema = CatalogSnapshotSchemaV3
	} else {
		doc.Schema = CatalogSnapshotSchemaV2
	}
	if err := validateCatalogDocument(doc); err != nil {
		return EffectiveMetadata{}, fmt.Errorf("%w: invalid effective metadata: %v", ErrArtifactCorrupt, err)
	}
	return EffectiveMetadata{digest: expected, bytes: append([]byte(nil), data...)}, nil
}

// CompileEffectiveMetadata produces the exact CatalogSnapshot artifact from a
// verified immutable input. It does not consult source checkouts, repositories,
// or another metadata registry.
func CompileEffectiveMetadata(source EffectiveMetadata) (Artifact, error) {
	verified, err := OpenEffectiveMetadata(source.digest, source.bytes)
	if err != nil {
		return Artifact{}, err
	}
	var doc catalogDocument
	if err := decodeStrict(verified.bytes, &doc); err != nil {
		return Artifact{}, fmt.Errorf("%w: decode verified effective metadata: %v", ErrArtifactCorrupt, err)
	}
	if doc.Schema == EffectiveMetadataSchemaV20 {
		doc.Schema = CatalogSnapshotSchemaV21
	} else if doc.Schema == EffectiveMetadataSchemaV19 {
		doc.Schema = CatalogSnapshotSchemaV20
	} else if doc.Schema == EffectiveMetadataSchemaV18 {
		doc.Schema = CatalogSnapshotSchemaV19
	} else if doc.Schema == EffectiveMetadataSchemaV17 {
		doc.Schema = CatalogSnapshotSchemaV18
	} else if doc.Schema == EffectiveMetadataSchemaV16 {
		doc.Schema = CatalogSnapshotSchemaV17
	} else if doc.Schema == EffectiveMetadataSchemaV15 {
		doc.Schema = CatalogSnapshotSchemaV16
	} else if doc.Schema == EffectiveMetadataSchemaV14 {
		doc.Schema = CatalogSnapshotSchemaV15
	} else if doc.Schema == EffectiveMetadataSchemaV13 {
		doc.Schema = CatalogSnapshotSchemaV14
	} else if doc.Schema == EffectiveMetadataSchemaV12 {
		doc.Schema = CatalogSnapshotSchemaV13
	} else if doc.Schema == EffectiveMetadataSchemaV11 {
		doc.Schema = CatalogSnapshotSchemaV12
	} else if doc.Schema == EffectiveMetadataSchemaV10 {
		doc.Schema = CatalogSnapshotSchemaV11
	} else if doc.Schema == EffectiveMetadataSchemaV9 {
		doc.Schema = CatalogSnapshotSchemaV10
	} else if doc.Schema == EffectiveMetadataSchemaV8 {
		doc.Schema = CatalogSnapshotSchemaV9
	} else if doc.Schema == EffectiveMetadataSchemaV7 {
		doc.Schema = CatalogSnapshotSchemaV8
	} else if doc.Schema == EffectiveMetadataSchemaV5 {
		doc.Schema = CatalogSnapshotSchemaV6
	} else if doc.Schema == EffectiveMetadataSchemaV4 {
		doc.Schema = CatalogSnapshotSchemaV5
	} else if doc.Schema == EffectiveMetadataSchemaV3 {
		doc.Schema = CatalogSnapshotSchemaV4
	} else if doc.Schema == EffectiveMetadataSchemaV2 {
		// V2 is the short-lived component-scoped mutation grammar. Its bytes
		// remain verifiable, but compilation migrates behavior to v4 and drops
		// the retired association rather than reactivating it.
		doc.Schema = CatalogSnapshotSchemaV4
		if doc.Mutation != nil {
			doc.Mutation.LegacyScopes = nil
		}
		for i := range doc.Projections {
			if doc.Projections[i].Profile == ProjectionRecordMutationV1 {
				doc.Projections[i].DefinitionComponentIDs = []string{}
			}
		}
	} else {
		doc.Schema = CatalogSnapshotSchemaV2
	}
	data, err := marshalCanonical(doc)
	if err != nil {
		return Artifact{}, conflict(ConflictEncoding, "catalogSnapshot", "encode canonical artifact: %v", err)
	}
	return makeArtifact(ArtifactCatalogSnapshot, data), nil
}
