/*
 * Copyright 2026 Oleg Borodin <onborodin@gmail.com>
 *
 * This work is published and licensed under a Creative Commons
 * Attribution-NonCommercial-NoDerivatives 4.0 International License.
 *
 * Distribution of this work is permitted, but commercial use and
 * modifications are strictly prohibited.
 */
package operator

import (
	"mstore/pkg/auxtool"
	"mstore/pkg/descr"
	"mstore/pkg/uuid"

	ocidigest "github.com/opencontainers/go-digest"
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)

func descrsFromManifest(name, reference string, manifest *ocispec.Manifest, rawManifest []byte) (descr.Manifest, []descr.Blob, error) {
	var err error
	manifestDescr := descr.Manifest{}
	//configDescr := descr.Blob{}
	blobDescrs := make([]descr.Blob, 0)

	timestamp := auxtool.TimeNow()

	// Make manifest descriptor
	manifestDigest := ocidigest.SHA256.FromBytes(rawManifest).String()
	manifestDescr = descr.Manifest{
		ID:          uuid.NewUUID(),
		Name:        name,
		Reference:   reference,
		Digest:      manifestDigest,
		ContentType: manifest.MediaType,
		Payload:     string(rawManifest),
		CreatedAt:   timestamp,
		UpdatedAt:   timestamp,
	}
	// Make config descriptor
	ociConfig := manifest.Config
	configDescr := descr.Blob{
		ID:        uuid.NewUUID(),
		Name:      name,
		Reference: reference,
		MediaType: ociConfig.MediaType,
		Digest:    string(ociConfig.Digest),
		Size:      ociConfig.Size,
		CreatedAt: timestamp,
		UpdatedAt: timestamp,
	}
	blobDescrs = append(blobDescrs, configDescr)
	// Make blob descriptions
	layerMap := make(map[string]bool)
	for _, layer := range manifest.Layers {
		blobDescr := descr.Blob{
			ID:        uuid.NewUUID(),
			Name:      name,
			Reference: reference,
			MediaType: layer.MediaType,
			Digest:    string(layer.Digest),
			Size:      layer.Size,
			CreatedAt: timestamp,
			UpdatedAt: timestamp,
		}
		_, alreadyAdded := layerMap[string(layer.Digest)]
		if !alreadyAdded {
			blobDescrs = append(blobDescrs, blobDescr)
			layerMap[string(layer.Digest)] = true
		}
	}
	return manifestDescr, blobDescrs, err
}

func layersDiff(name, reference string, existingManifest, incomingManifest *ocispec.Manifest, rawManifest []byte) ([]descr.Blob, []descr.Blob, error) {
	var err error
	newBlobDescrs := make([]descr.Blob, 0)
	delBlobDescrs := make([]descr.Blob, 0)

	// Calculating old layers
	delLayers := make([]ocispec.Descriptor, 0)
	for _, oldLayer := range existingManifest.Layers {
		uniq := true
		for _, newLayer := range incomingManifest.Layers {
			if oldLayer.Digest == newLayer.Digest {
				uniq = false
			}
		}
		if uniq {
			delLayers = append(delLayers, oldLayer)
		}
	}
	// Calculating new layers
	newLayers := make([]ocispec.Descriptor, 0)
	for _, newLayer := range incomingManifest.Layers {
		uniq := true
		for _, oldLayer := range existingManifest.Layers {
			if oldLayer.Digest == newLayer.Digest {
				uniq = false
			}
		}
		if uniq {
			newLayers = append(newLayers, newLayer)
		}
	}
	if incomingManifest.Config.Digest != existingManifest.Config.Digest {
		delLayers = append(delLayers, existingManifest.Config)
		newLayers = append(newLayers, incomingManifest.Config)
	}
	// Converting to new blob
	timestamp := auxtool.TimeNow()
	for _, layer := range newLayers {
		blobDescr := descr.Blob{
			ID:        uuid.NewUUID(),
			Name:      name,
			Reference: reference,
			MediaType: layer.MediaType,
			Digest:    string(layer.Digest),
			Size:      layer.Size,
			CreatedAt: timestamp,
			UpdatedAt: timestamp,
		}
		newBlobDescrs = append(newBlobDescrs, blobDescr)
	}
	// Converting to old blobs
	for _, layer := range delLayers {
		blobDescr := descr.Blob{
			ID:        uuid.NewUUID(),
			Name:      name,
			Reference: reference,
			MediaType: layer.MediaType,
			Digest:    string(layer.Digest),
			Size:      layer.Size,
		}
		delBlobDescrs = append(delBlobDescrs, blobDescr)
	}
	return newBlobDescrs, delBlobDescrs, err
}
