//go:build linux && cgo && !agent

package cluster

// The code below was generated by incus-generate - DO NOT EDIT!

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"net/http"
	"strings"

	"github.com/lxc/incus/internal/server/db/query"
	"github.com/lxc/incus/shared/api"
)

var _ = api.ServerEnvironment{}

var nodeClusterGroupObjects = RegisterStmt(`
SELECT nodes_cluster_groups.group_id, nodes.name AS node
  FROM nodes_cluster_groups
  JOIN nodes ON nodes_cluster_groups.node_id = nodes.id
  ORDER BY nodes_cluster_groups.group_id
`)

var nodeClusterGroupObjectsByGroupID = RegisterStmt(`
SELECT nodes_cluster_groups.group_id, nodes.name AS node
  FROM nodes_cluster_groups
  JOIN nodes ON nodes_cluster_groups.node_id = nodes.id
  WHERE ( nodes_cluster_groups.group_id = ? )
  ORDER BY nodes_cluster_groups.group_id
`)

var nodeClusterGroupID = RegisterStmt(`
SELECT nodes_cluster_groups.id FROM nodes_cluster_groups
  WHERE nodes_cluster_groups.group_id = ?
`)

var nodeClusterGroupCreate = RegisterStmt(`
INSERT INTO nodes_cluster_groups (group_id, node_id)
  VALUES (?, (SELECT nodes.id FROM nodes WHERE nodes.name = ?))
`)

var nodeClusterGroupDeleteByGroupID = RegisterStmt(`
DELETE FROM nodes_cluster_groups WHERE group_id = ?
`)

// nodeClusterGroupColumns returns a string of column names to be used with a SELECT statement for the entity.
// Use this function when building statements to retrieve database entries matching the NodeClusterGroup entity.
func nodeClusterGroupColumns() string {
	return "nodes_clusters_groups.group_id, nodes.name AS node"
}

// getNodeClusterGroups can be used to run handwritten sql.Stmts to return a slice of objects.
func getNodeClusterGroups(ctx context.Context, stmt *sql.Stmt, args ...any) ([]NodeClusterGroup, error) {
	objects := make([]NodeClusterGroup, 0)

	dest := func(scan func(dest ...any) error) error {
		n := NodeClusterGroup{}
		err := scan(&n.GroupID, &n.Node)
		if err != nil {
			return err
		}

		objects = append(objects, n)

		return nil
	}

	err := query.SelectObjects(ctx, stmt, dest, args...)
	if err != nil {
		return nil, fmt.Errorf("Failed to fetch from \"nodes_clusters_groups\" table: %w", err)
	}

	return objects, nil
}

// getNodeClusterGroupsRaw can be used to run handwritten query strings to return a slice of objects.
func getNodeClusterGroupsRaw(ctx context.Context, tx *sql.Tx, sql string, args ...any) ([]NodeClusterGroup, error) {
	objects := make([]NodeClusterGroup, 0)

	dest := func(scan func(dest ...any) error) error {
		n := NodeClusterGroup{}
		err := scan(&n.GroupID, &n.Node)
		if err != nil {
			return err
		}

		objects = append(objects, n)

		return nil
	}

	err := query.Scan(ctx, tx, sql, dest, args...)
	if err != nil {
		return nil, fmt.Errorf("Failed to fetch from \"nodes_clusters_groups\" table: %w", err)
	}

	return objects, nil
}

// GetNodeClusterGroups returns all available node_cluster_groups.
// generator: node_cluster_group GetMany
func GetNodeClusterGroups(ctx context.Context, tx *sql.Tx, filters ...NodeClusterGroupFilter) ([]NodeClusterGroup, error) {
	var err error

	// Result slice.
	objects := make([]NodeClusterGroup, 0)

	// Pick the prepared statement and arguments to use based on active criteria.
	var sqlStmt *sql.Stmt
	args := []any{}
	queryParts := [2]string{}

	if len(filters) == 0 {
		sqlStmt, err = Stmt(tx, nodeClusterGroupObjects)
		if err != nil {
			return nil, fmt.Errorf("Failed to get \"nodeClusterGroupObjects\" prepared statement: %w", err)
		}
	}

	for i, filter := range filters {
		if filter.GroupID != nil {
			args = append(args, []any{filter.GroupID}...)
			if len(filters) == 1 {
				sqlStmt, err = Stmt(tx, nodeClusterGroupObjectsByGroupID)
				if err != nil {
					return nil, fmt.Errorf("Failed to get \"nodeClusterGroupObjectsByGroupID\" prepared statement: %w", err)
				}

				break
			}

			query, err := StmtString(nodeClusterGroupObjectsByGroupID)
			if err != nil {
				return nil, fmt.Errorf("Failed to get \"nodeClusterGroupObjects\" prepared statement: %w", err)
			}

			parts := strings.SplitN(query, "ORDER BY", 2)
			if i == 0 {
				copy(queryParts[:], parts)
				continue
			}

			_, where, _ := strings.Cut(parts[0], "WHERE")
			queryParts[0] += "OR" + where
		} else if filter.GroupID == nil {
			return nil, fmt.Errorf("Cannot filter on empty NodeClusterGroupFilter")
		} else {
			return nil, fmt.Errorf("No statement exists for the given Filter")
		}
	}

	// Select.
	if sqlStmt != nil {
		objects, err = getNodeClusterGroups(ctx, sqlStmt, args...)
	} else {
		queryStr := strings.Join(queryParts[:], "ORDER BY")
		objects, err = getNodeClusterGroupsRaw(ctx, tx, queryStr, args...)
	}

	if err != nil {
		return nil, fmt.Errorf("Failed to fetch from \"nodes_clusters_groups\" table: %w", err)
	}

	return objects, nil
}

// CreateNodeClusterGroup adds a new node_cluster_group to the database.
// generator: node_cluster_group Create
func CreateNodeClusterGroup(ctx context.Context, tx *sql.Tx, object NodeClusterGroup) (int64, error) {
	// Check if a node_cluster_group with the same key exists.
	exists, err := NodeClusterGroupExists(ctx, tx, object.GroupID)
	if err != nil {
		return -1, fmt.Errorf("Failed to check for duplicates: %w", err)
	}

	if exists {
		return -1, api.StatusErrorf(http.StatusConflict, "This \"nodes_clusters_groups\" entry already exists")
	}

	args := make([]any, 2)

	// Populate the statement arguments.
	args[0] = object.GroupID
	args[1] = object.Node

	// Prepared statement to use.
	stmt, err := Stmt(tx, nodeClusterGroupCreate)
	if err != nil {
		return -1, fmt.Errorf("Failed to get \"nodeClusterGroupCreate\" prepared statement: %w", err)
	}

	// Execute the statement.
	result, err := stmt.Exec(args...)
	if err != nil {
		return -1, fmt.Errorf("Failed to create \"nodes_clusters_groups\" entry: %w", err)
	}

	id, err := result.LastInsertId()
	if err != nil {
		return -1, fmt.Errorf("Failed to fetch \"nodes_clusters_groups\" entry ID: %w", err)
	}

	return id, nil
}

// NodeClusterGroupExists checks if a node_cluster_group with the given key exists.
// generator: node_cluster_group Exists
func NodeClusterGroupExists(ctx context.Context, tx *sql.Tx, groupID int) (bool, error) {
	_, err := GetNodeClusterGroupID(ctx, tx, groupID)
	if err != nil {
		if api.StatusErrorCheck(err, http.StatusNotFound) {
			return false, nil
		}

		return false, err
	}

	return true, nil
}

// GetNodeClusterGroupID return the ID of the node_cluster_group with the given key.
// generator: node_cluster_group ID
func GetNodeClusterGroupID(ctx context.Context, tx *sql.Tx, groupID int) (int64, error) {
	stmt, err := Stmt(tx, nodeClusterGroupID)
	if err != nil {
		return -1, fmt.Errorf("Failed to get \"nodeClusterGroupID\" prepared statement: %w", err)
	}

	row := stmt.QueryRowContext(ctx, groupID)
	var id int64
	err = row.Scan(&id)
	if errors.Is(err, sql.ErrNoRows) {
		return -1, api.StatusErrorf(http.StatusNotFound, "NodeClusterGroup not found")
	}

	if err != nil {
		return -1, fmt.Errorf("Failed to get \"nodes_clusters_groups\" ID: %w", err)
	}

	return id, nil
}

// DeleteNodeClusterGroup deletes the node_cluster_group matching the given key parameters.
// generator: node_cluster_group DeleteOne-by-GroupID
func DeleteNodeClusterGroup(ctx context.Context, tx *sql.Tx, groupID int) error {
	stmt, err := Stmt(tx, nodeClusterGroupDeleteByGroupID)
	if err != nil {
		return fmt.Errorf("Failed to get \"nodeClusterGroupDeleteByGroupID\" prepared statement: %w", err)
	}

	result, err := stmt.Exec(groupID)
	if err != nil {
		return fmt.Errorf("Delete \"nodes_clusters_groups\": %w", err)
	}

	n, err := result.RowsAffected()
	if err != nil {
		return fmt.Errorf("Fetch affected rows: %w", err)
	}

	if n == 0 {
		return api.StatusErrorf(http.StatusNotFound, "NodeClusterGroup not found")
	} else if n > 1 {
		return fmt.Errorf("Query deleted %d NodeClusterGroup rows instead of 1", n)
	}

	return nil
}
