/*
 * Copyright 2026 Oleg Borodin <onborodin@gmail.com>
 */
package imageoper

import (
	"encoding/json"

	//"mstore/pkg/auxoci"
	"mstore/pkg/auxtool"
	"mstore/pkg/auxuuid"
	"mstore/pkg/descr"

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

func indexFromManigestDescrs(mandescrs []descr.Manifest) (ocispec.Index, []byte, error) {
	var err error
	var indexdata []byte
	index := ocispec.Index{
		MediaType: oiiMediaType,
		Manifests: make([]ocispec.Descriptor, 0),
	}
	index.Versioned.SchemaVersion = 2
	for _, mandescr := range mandescrs {
		var man ocispec.Manifest

		if mandescr.ContentType != oimMediaType && mandescr.ContentType != ddmMediaType {
			continue
		}

		man.Subject = &ocispec.Descriptor{}
		man.Subject.Platform = &ocispec.Platform{}
		man.Config = ocispec.Descriptor{}
		man.Config.Platform = &ocispec.Platform{}

		err = json.Unmarshal([]byte(mandescr.Payload), &man)
		if err != nil {
			return index, indexdata, err
		}
		digobj, err := ocidigest.Parse(mandescr.Digest)
		if err != nil {
			return index, indexdata, err
		}
		size := int64(len(mandescr.Payload))
		descriptor := ocispec.Descriptor{
			MediaType: mandescr.ContentType,
			Digest:    digobj,
			Size:      size,
			Platform:  man.Subject.Platform,
		}
		index.Manifests = append(index.Manifests, descriptor)
	}
	indexdata, err = json.Marshal(index)
	if err != nil {
		return index, indexdata, err
	}
	indexdata = indexdata
	return index, indexdata, err
}

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

	timestamp := auxtool.TimeNow()

	// Make manifest descriptor
	manifestDigest := ocidigest.SHA256.FromBytes(rawManifest).String()
	mandescr = descr.Manifest{
		ID:          auxuuid.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:        auxuuid.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:        auxuuid.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 mandescr, 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:        auxuuid.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:        auxuuid.NewUUID(),
			Name:      name,
			Reference: reference,
			MediaType: layer.MediaType,
			Digest:    string(layer.Digest),
			Size:      layer.Size,
		}
		delBlobDescrs = append(delBlobDescrs, blobDescr)
	}
	return newBlobDescrs, delBlobDescrs, err
}
