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

import (
	"context"

	"mstore/pkg/descr"
)

func (db *Database) InsertManifest(ctx context.Context, manifest *descr.Manifest) error {
	var err error
	var request string
	request = `INSERT INTO manifests(id, name, reference, contentType, payload, digest,
                created_at, updated_at, created_by, updated_by, architecture, os, variant)
                        VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)`
	_, err = db.db.Exec(request, manifest.ID, manifest.Name, manifest.Reference,
		manifest.ContentType, manifest.Payload, manifest.Digest,
		manifest.CreatedAt, manifest.UpdatedAt,
		manifest.CreatedBy, manifest.UpdatedBy,
		manifest.Architecture, manifest.OS, manifest.Variant)
	if err != nil {
		return err
	}
	return err
}

func (db *Database) InsertManifests(ctx context.Context, manifests []*descr.Manifest) error {
	var err error

	// Begin Tx
	tx, err := db.db.BeginTx(ctx, nil)
	for _, manifest := range manifests {
		var request string
		request = `INSERT INTO manifests(id, name, reference, contentType, payload, digest,
                                        created_at, updated_at, created_by, updated_by,
                                        architecture, os, variant)
                                VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)`
		_, err = tx.Exec(request, manifest.ID, manifest.Name, manifest.Reference,
			manifest.ContentType, manifest.Payload, manifest.Digest,
			manifest.CreatedAt, manifest.UpdatedAt,
			manifest.CreatedBy, manifest.UpdatedBy,
			manifest.Architecture, manifest.OS, manifest.Variant)
		if err != nil {
			return err
		}
	}
	// Commit
	err = tx.Commit()
	if err != nil {
		return err
	}
	return err
}

func (db *Database) UpdateManifest(ctx context.Context, manifest *descr.Manifest) error {
	var err error
	var request string
	// Manifest
	request = `UPDATE manifests SET contentType = $1, payload = $2, digest = $3, updated_at = $4
                   WHERE name = $5 AND reference = $6, architecture = $7, os = $8, variant = $9`
	_, err = db.db.Exec(request, manifest.ContentType, manifest.Payload, manifest.Digest,
		manifest.UpdatedAt, manifest.Name, manifest.Reference,
		manifest.Architecture, manifest.OS, manifest.Variant)
	if err != nil {
		return err
	}
	return err
}

func (db *Database) InsertManifestWithLayers(ctx context.Context, manifest *descr.Manifest, layers []descr.Blob) error {
	var err error
	var request string

	// Begin Tx
	tx, err := db.db.BeginTx(ctx, nil)
	// Manifest
	request = `
                INSERT INTO manifests(id, name, reference, contentType, payload, digest,
                        created_at, updated_at, created_by, updated_by, architecture, os, variant)
                        VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13);`
	_, err = tx.Exec(request, manifest.ID, manifest.Name, manifest.Reference,
		manifest.ContentType, manifest.Payload, manifest.Digest,
		manifest.CreatedAt, manifest.UpdatedAt,
		manifest.CreatedBy, manifest.UpdatedBy,
		manifest.Architecture, manifest.OS, manifest.Variant)
	if err != nil {
		return err
	}
	// Layers
	for _, layer := range layers {
		request = `
                        INSERT INTO blobs(id, name, reference, mediaType, digest, size,
                                created_at, updated_at, created_by, updated_by)
                                VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`
		_, err = tx.Exec(request,
			layer.ID, layer.Name, layer.Reference, layer.MediaType,
			layer.Digest, layer.Size,
			layer.CreatedAt, layer.UpdatedAt,
			layer.CreatedBy, layer.UpdatedBy)
		if err != nil {
			return err
		}
	}
	// Commit
	err = tx.Commit()
	if err != nil {
		return err
	}
	// TODO: tx.Rollback()
	return err
}

func (db *Database) UpdateManifestWithBlobs(ctx context.Context, manifest *descr.Manifest,
	newLayers []descr.Blob, oldLayers []descr.Blob) error {
	var err error
	var request string

	// Begin Tx
	tx, err := db.db.BeginTx(ctx, nil)
	// TODO: tx.Rollback()

	// Manifest
	request = `UPDATE manifests SET contentType = $1, payload = $2, digest = $3, updated_at = $4, updated_by = $5
                        WHERE name = $6 AND reference = $7
                          AND architecture = $8 AND os = $9 AND variant = $10`
	_, err = tx.Exec(request, manifest.ContentType, manifest.Payload, manifest.Digest,
		manifest.UpdatedAt, manifest.UpdatedBy,
		manifest.Name, manifest.Reference,
		manifest.Architecture, manifest.OS, manifest.Variant)
	if err != nil {
		return err
	}
	// New layers
	for _, layer := range newLayers {
		request = `INSERT INTO blobs(id, name, reference, mediaType, digest, size,
                                created_at, updated_at, created_by, updated_by)
                                VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`
		_, err = tx.Exec(request, layer.ID, layer.Name, layer.Reference, layer.MediaType,
			layer.Digest, layer.Size, layer.CreatedAt, layer.UpdatedAt)
		if err != nil {
			return err
		}
	}
	// Old layers
	for _, layer := range oldLayers {
		request = `DELETE FROM blobs WHERE name == $1 AND reference == $2 AND digest == $3`
		_, err = tx.Exec(request, layer.Name, layer.Reference, layer.Digest)
	}
	// Commit
	err = tx.Commit()
	if err != nil {
		return err
	}
	return err
}

func (db *Database) ManifestExistsByReference(ctx context.Context, name, reference string) (bool, error) {
	var err error
	var count int
	var exists bool
	request := `SELECT count(id) AS count FROM manifests WHERE name = $1 AND reference = $2 LIMIT 1`
	err = db.db.Get(&count, request, name, reference)
	if err != nil {
		return exists, err
	}
	if count > 0 {
		exists = true
	}
	return exists, err
}

func (db *Database) ManifestExistsByDigest(ctx context.Context, name, digest string) (bool, error) {
	var err error
	var count int
	var exists bool
	request := `SELECT count(id) AS count FROM manifests WHERE name = $1 AND digest = $2 LIMIT 1`
	err = db.db.Get(&count, request, name, digest)
	if err != nil {
		return exists, err
	}
	if count > 0 {
		exists = true
	}
	return exists, err
}

func (db *Database) GetManifestByDigest(ctx context.Context, name, digest string) (bool, descr.Manifest, error) {
	var err error
	manifest := descr.Manifest{}
	manifests := make([]descr.Manifest, 0)
	exists := false
	request := `SELECT * FROM manifests WHERE name = $1 AND digest = $2 LIMIT 1`
	err = db.db.Select(&manifests, request, name, digest)
	if err != nil {
		return exists, manifest, err
	}
	if len(manifests) > 0 {
		exists = true
		manifest = manifests[0]
	}
	return exists, manifest, err
}

func (db *Database) GetManifestByReference(ctx context.Context, name, reference string) (bool, descr.Manifest, error) {
	var err error
	exists := false
	manifest := descr.Manifest{}
	manifests := make([]descr.Manifest, 0)
	request := `SELECT * FROM manifests WHERE name = $1 AND reference = $2 LIMIT 1`
	err = db.db.Select(&manifests, request, name, reference)
	if err != nil {
		return exists, manifest, err
	}
	if len(manifests) > 0 {
		manifest = manifests[0]
		exists = true
	}
	return exists, manifest, err
}

func (db *Database) xxxGetManifestsByReferenceArchitecture(ctx context.Context, name, reference, architecture, os, variant string) (bool, descr.Manifest, error) {
	var err error
	exists := false
	manifest := descr.Manifest{}
	manifests := make([]descr.Manifest, 0)
	request := `SELECT * FROM manifests WHERE name = $1 AND reference = $2
                        AND architecture = $3 AND os = $4 AND variant = $5 LIMIT 1000`
	err = db.db.Select(&manifests, request, name, reference, architecture, os, variant)
	if err != nil {
		return exists, manifest, err
	}

	if len(manifests) > 0 {
		manifest = manifests[0]
		exists = true
	}
	return exists, manifest, err
}

func (db *Database) ListManifestsByName(ctx context.Context, name string) ([]descr.Manifest, error) {
	var err error
	manifests := make([]descr.Manifest, 0)
	request := `SELECT id, name, reference, contentType, payload FROM manifests WHERE name = $1`
	err = db.db.Select(&manifests, request, name)
	if err != nil {
		return manifests, err
	}
	return manifests, err
}

func (db *Database) ListAllManifests(ctx context.Context) ([]descr.Manifest, error) {
	var err error
	manifests := make([]descr.Manifest, 0)
	request := `SELECT * FROM manifests`
	err = db.db.Select(&manifests, request)
	if err != nil {
		return manifests, err
	}
	return manifests, err
}

func (db *Database) GetReferer(ctx context.Context, name, digest string) ([]descr.Manifest, error) {
	var err error
	manifests := make([]descr.Manifest, 0)
	request := `SELECT * FROM manifests WHERE name = $1, digest = $2`
	err = db.db.Select(&manifests, request, name, digest)
	if err != nil {
		return manifests, err
	}
	return manifests, err
}

func (db *Database) DeleteManifest(ctx context.Context, name, reference string) error {
	var err error
	var request string

	// Begin Tx
	tx, err := db.db.BeginTx(ctx, nil)
	// TODO: tx.Rollback()

	// Blobs
	request = `DELETE FROM blobs WHERE name = $1 AND reference = $2`
	_, err = tx.Exec(request, name, reference)
	if err != nil {
		return err
	}
	// Manifest
	request = `DELETE FROM manifests WHERE name = $1 AND reference = $2`
	_, err = tx.Exec(request, name, reference)
	if err != nil {
		return err
	}
	// Commit
	err = tx.Commit()
	if err != nil {
		return err
	}
	return err
}
