// Code generated by ent, DO NOT EDIT.

package ent

import (
	"context"
	"fmt"

	"entgo.io/contrib/entgql"
	"github.com/99designs/gqlgen/graphql"
	"github.com/google/uuid"
	"github.com/hashicorp/go-multierror"
	"github.com/in-toto/archivista/ent/attestation"
	"github.com/in-toto/archivista/ent/attestationcollection"
	"github.com/in-toto/archivista/ent/attestationpolicy"
	"github.com/in-toto/archivista/ent/dsse"
	"github.com/in-toto/archivista/ent/payloaddigest"
	"github.com/in-toto/archivista/ent/signature"
	"github.com/in-toto/archivista/ent/statement"
	"github.com/in-toto/archivista/ent/subject"
	"github.com/in-toto/archivista/ent/subjectdigest"
	"github.com/in-toto/archivista/ent/timestamp"
)

// Noder wraps the basic Node method.
type Noder interface {
	IsNode()
}

var attestationImplementors = []string{"Attestation", "Node"}

// IsNode implements the Node interface check for GQLGen.
func (*Attestation) IsNode() {}

var attestationcollectionImplementors = []string{"AttestationCollection", "Node"}

// IsNode implements the Node interface check for GQLGen.
func (*AttestationCollection) IsNode() {}

var attestationpolicyImplementors = []string{"AttestationPolicy", "Node"}

// IsNode implements the Node interface check for GQLGen.
func (*AttestationPolicy) IsNode() {}

var dsseImplementors = []string{"Dsse", "Node"}

// IsNode implements the Node interface check for GQLGen.
func (*Dsse) IsNode() {}

var payloaddigestImplementors = []string{"PayloadDigest", "Node"}

// IsNode implements the Node interface check for GQLGen.
func (*PayloadDigest) IsNode() {}

var signatureImplementors = []string{"Signature", "Node"}

// IsNode implements the Node interface check for GQLGen.
func (*Signature) IsNode() {}

var statementImplementors = []string{"Statement", "Node"}

// IsNode implements the Node interface check for GQLGen.
func (*Statement) IsNode() {}

var subjectImplementors = []string{"Subject", "Node"}

// IsNode implements the Node interface check for GQLGen.
func (*Subject) IsNode() {}

var subjectdigestImplementors = []string{"SubjectDigest", "Node"}

// IsNode implements the Node interface check for GQLGen.
func (*SubjectDigest) IsNode() {}

var timestampImplementors = []string{"Timestamp", "Node"}

// IsNode implements the Node interface check for GQLGen.
func (*Timestamp) IsNode() {}

var errNodeInvalidID = &NotFoundError{"node"}

// NodeOption allows configuring the Noder execution using functional options.
type NodeOption func(*nodeOptions)

// WithNodeType sets the node Type resolver function (i.e. the table to query).
// If was not provided, the table will be derived from the universal-id
// configuration as described in: https://entgo.io/docs/migrate/#universal-ids.
func WithNodeType(f func(context.Context, uuid.UUID) (string, error)) NodeOption {
	return func(o *nodeOptions) {
		o.nodeType = f
	}
}

// WithFixedNodeType sets the Type of the node to a fixed value.
func WithFixedNodeType(t string) NodeOption {
	return WithNodeType(func(context.Context, uuid.UUID) (string, error) {
		return t, nil
	})
}

type nodeOptions struct {
	nodeType func(context.Context, uuid.UUID) (string, error)
}

func (c *Client) newNodeOpts(opts []NodeOption) *nodeOptions {
	nopts := &nodeOptions{}
	for _, opt := range opts {
		opt(nopts)
	}
	if nopts.nodeType == nil {
		nopts.nodeType = func(ctx context.Context, id uuid.UUID) (string, error) {
			return "", fmt.Errorf("cannot resolve noder (%v) without its type", id)
		}
	}
	return nopts
}

// Noder returns a Node by its id. If the NodeType was not provided, it will
// be derived from the id value according to the universal-id configuration.
//
//	c.Noder(ctx, id)
//	c.Noder(ctx, id, ent.WithNodeType(typeResolver))
func (c *Client) Noder(ctx context.Context, id uuid.UUID, opts ...NodeOption) (_ Noder, err error) {
	defer func() {
		if IsNotFound(err) {
			err = multierror.Append(err, entgql.ErrNodeNotFound(id))
		}
	}()
	table, err := c.newNodeOpts(opts).nodeType(ctx, id)
	if err != nil {
		return nil, err
	}
	return c.noder(ctx, table, id)
}

func (c *Client) noder(ctx context.Context, table string, id uuid.UUID) (Noder, error) {
	switch table {
	case attestation.Table:
		query := c.Attestation.Query().
			Where(attestation.ID(id))
		if fc := graphql.GetFieldContext(ctx); fc != nil {
			if err := query.collectField(ctx, true, graphql.GetOperationContext(ctx), fc.Field, nil, attestationImplementors...); err != nil {
				return nil, err
			}
		}
		return query.Only(ctx)
	case attestationcollection.Table:
		query := c.AttestationCollection.Query().
			Where(attestationcollection.ID(id))
		if fc := graphql.GetFieldContext(ctx); fc != nil {
			if err := query.collectField(ctx, true, graphql.GetOperationContext(ctx), fc.Field, nil, attestationcollectionImplementors...); err != nil {
				return nil, err
			}
		}
		return query.Only(ctx)
	case attestationpolicy.Table:
		query := c.AttestationPolicy.Query().
			Where(attestationpolicy.ID(id))
		if fc := graphql.GetFieldContext(ctx); fc != nil {
			if err := query.collectField(ctx, true, graphql.GetOperationContext(ctx), fc.Field, nil, attestationpolicyImplementors...); err != nil {
				return nil, err
			}
		}
		return query.Only(ctx)
	case dsse.Table:
		query := c.Dsse.Query().
			Where(dsse.ID(id))
		if fc := graphql.GetFieldContext(ctx); fc != nil {
			if err := query.collectField(ctx, true, graphql.GetOperationContext(ctx), fc.Field, nil, dsseImplementors...); err != nil {
				return nil, err
			}
		}
		return query.Only(ctx)
	case payloaddigest.Table:
		query := c.PayloadDigest.Query().
			Where(payloaddigest.ID(id))
		if fc := graphql.GetFieldContext(ctx); fc != nil {
			if err := query.collectField(ctx, true, graphql.GetOperationContext(ctx), fc.Field, nil, payloaddigestImplementors...); err != nil {
				return nil, err
			}
		}
		return query.Only(ctx)
	case signature.Table:
		query := c.Signature.Query().
			Where(signature.ID(id))
		if fc := graphql.GetFieldContext(ctx); fc != nil {
			if err := query.collectField(ctx, true, graphql.GetOperationContext(ctx), fc.Field, nil, signatureImplementors...); err != nil {
				return nil, err
			}
		}
		return query.Only(ctx)
	case statement.Table:
		query := c.Statement.Query().
			Where(statement.ID(id))
		if fc := graphql.GetFieldContext(ctx); fc != nil {
			if err := query.collectField(ctx, true, graphql.GetOperationContext(ctx), fc.Field, nil, statementImplementors...); err != nil {
				return nil, err
			}
		}
		return query.Only(ctx)
	case subject.Table:
		query := c.Subject.Query().
			Where(subject.ID(id))
		if fc := graphql.GetFieldContext(ctx); fc != nil {
			if err := query.collectField(ctx, true, graphql.GetOperationContext(ctx), fc.Field, nil, subjectImplementors...); err != nil {
				return nil, err
			}
		}
		return query.Only(ctx)
	case subjectdigest.Table:
		query := c.SubjectDigest.Query().
			Where(subjectdigest.ID(id))
		if fc := graphql.GetFieldContext(ctx); fc != nil {
			if err := query.collectField(ctx, true, graphql.GetOperationContext(ctx), fc.Field, nil, subjectdigestImplementors...); err != nil {
				return nil, err
			}
		}
		return query.Only(ctx)
	case timestamp.Table:
		query := c.Timestamp.Query().
			Where(timestamp.ID(id))
		if fc := graphql.GetFieldContext(ctx); fc != nil {
			if err := query.collectField(ctx, true, graphql.GetOperationContext(ctx), fc.Field, nil, timestampImplementors...); err != nil {
				return nil, err
			}
		}
		return query.Only(ctx)
	default:
		return nil, fmt.Errorf("cannot resolve noder from table %q: %w", table, errNodeInvalidID)
	}
}

func (c *Client) Noders(ctx context.Context, ids []uuid.UUID, opts ...NodeOption) ([]Noder, error) {
	switch len(ids) {
	case 1:
		noder, err := c.Noder(ctx, ids[0], opts...)
		if err != nil {
			return nil, err
		}
		return []Noder{noder}, nil
	case 0:
		return []Noder{}, nil
	}

	noders := make([]Noder, len(ids))
	errors := make([]error, len(ids))
	tables := make(map[string][]uuid.UUID)
	id2idx := make(map[uuid.UUID][]int, len(ids))
	nopts := c.newNodeOpts(opts)
	for i, id := range ids {
		table, err := nopts.nodeType(ctx, id)
		if err != nil {
			errors[i] = err
			continue
		}
		tables[table] = append(tables[table], id)
		id2idx[id] = append(id2idx[id], i)
	}

	for table, ids := range tables {
		nodes, err := c.noders(ctx, table, ids)
		if err != nil {
			for _, id := range ids {
				for _, idx := range id2idx[id] {
					errors[idx] = err
				}
			}
		} else {
			for i, id := range ids {
				for _, idx := range id2idx[id] {
					noders[idx] = nodes[i]
				}
			}
		}
	}

	for i, id := range ids {
		if errors[i] == nil {
			if noders[i] != nil {
				continue
			}
			errors[i] = entgql.ErrNodeNotFound(id)
		} else if IsNotFound(errors[i]) {
			errors[i] = multierror.Append(errors[i], entgql.ErrNodeNotFound(id))
		}
		ctx := graphql.WithPathContext(ctx,
			graphql.NewPathWithIndex(i),
		)
		graphql.AddError(ctx, errors[i])
	}
	return noders, nil
}

func (c *Client) noders(ctx context.Context, table string, ids []uuid.UUID) ([]Noder, error) {
	noders := make([]Noder, len(ids))
	idmap := make(map[uuid.UUID][]*Noder, len(ids))
	for i, id := range ids {
		idmap[id] = append(idmap[id], &noders[i])
	}
	switch table {
	case attestation.Table:
		query := c.Attestation.Query().
			Where(attestation.IDIn(ids...))
		query, err := query.CollectFields(ctx, attestationImplementors...)
		if err != nil {
			return nil, err
		}
		nodes, err := query.All(ctx)
		if err != nil {
			return nil, err
		}
		for _, node := range nodes {
			for _, noder := range idmap[node.ID] {
				*noder = node
			}
		}
	case attestationcollection.Table:
		query := c.AttestationCollection.Query().
			Where(attestationcollection.IDIn(ids...))
		query, err := query.CollectFields(ctx, attestationcollectionImplementors...)
		if err != nil {
			return nil, err
		}
		nodes, err := query.All(ctx)
		if err != nil {
			return nil, err
		}
		for _, node := range nodes {
			for _, noder := range idmap[node.ID] {
				*noder = node
			}
		}
	case attestationpolicy.Table:
		query := c.AttestationPolicy.Query().
			Where(attestationpolicy.IDIn(ids...))
		query, err := query.CollectFields(ctx, attestationpolicyImplementors...)
		if err != nil {
			return nil, err
		}
		nodes, err := query.All(ctx)
		if err != nil {
			return nil, err
		}
		for _, node := range nodes {
			for _, noder := range idmap[node.ID] {
				*noder = node
			}
		}
	case dsse.Table:
		query := c.Dsse.Query().
			Where(dsse.IDIn(ids...))
		query, err := query.CollectFields(ctx, dsseImplementors...)
		if err != nil {
			return nil, err
		}
		nodes, err := query.All(ctx)
		if err != nil {
			return nil, err
		}
		for _, node := range nodes {
			for _, noder := range idmap[node.ID] {
				*noder = node
			}
		}
	case payloaddigest.Table:
		query := c.PayloadDigest.Query().
			Where(payloaddigest.IDIn(ids...))
		query, err := query.CollectFields(ctx, payloaddigestImplementors...)
		if err != nil {
			return nil, err
		}
		nodes, err := query.All(ctx)
		if err != nil {
			return nil, err
		}
		for _, node := range nodes {
			for _, noder := range idmap[node.ID] {
				*noder = node
			}
		}
	case signature.Table:
		query := c.Signature.Query().
			Where(signature.IDIn(ids...))
		query, err := query.CollectFields(ctx, signatureImplementors...)
		if err != nil {
			return nil, err
		}
		nodes, err := query.All(ctx)
		if err != nil {
			return nil, err
		}
		for _, node := range nodes {
			for _, noder := range idmap[node.ID] {
				*noder = node
			}
		}
	case statement.Table:
		query := c.Statement.Query().
			Where(statement.IDIn(ids...))
		query, err := query.CollectFields(ctx, statementImplementors...)
		if err != nil {
			return nil, err
		}
		nodes, err := query.All(ctx)
		if err != nil {
			return nil, err
		}
		for _, node := range nodes {
			for _, noder := range idmap[node.ID] {
				*noder = node
			}
		}
	case subject.Table:
		query := c.Subject.Query().
			Where(subject.IDIn(ids...))
		query, err := query.CollectFields(ctx, subjectImplementors...)
		if err != nil {
			return nil, err
		}
		nodes, err := query.All(ctx)
		if err != nil {
			return nil, err
		}
		for _, node := range nodes {
			for _, noder := range idmap[node.ID] {
				*noder = node
			}
		}
	case subjectdigest.Table:
		query := c.SubjectDigest.Query().
			Where(subjectdigest.IDIn(ids...))
		query, err := query.CollectFields(ctx, subjectdigestImplementors...)
		if err != nil {
			return nil, err
		}
		nodes, err := query.All(ctx)
		if err != nil {
			return nil, err
		}
		for _, node := range nodes {
			for _, noder := range idmap[node.ID] {
				*noder = node
			}
		}
	case timestamp.Table:
		query := c.Timestamp.Query().
			Where(timestamp.IDIn(ids...))
		query, err := query.CollectFields(ctx, timestampImplementors...)
		if err != nil {
			return nil, err
		}
		nodes, err := query.All(ctx)
		if err != nil {
			return nil, err
		}
		for _, node := range nodes {
			for _, noder := range idmap[node.ID] {
				*noder = node
			}
		}
	default:
		return nil, fmt.Errorf("cannot resolve noders from table %q: %w", table, errNodeInvalidID)
	}
	return noders, nil
}
