package database

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

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

	"github.com/canonical/lxd/lxd/db/query"
	"github.com/canonical/lxd/shared/api"
	"github.com/canonical/microcluster/v2/cluster"
)

var _ = api.ServerEnvironment{}

var groupedServiceObjects = cluster.RegisterStmt(`
SELECT grouped_services.id, service_groups.service AS service, service_groups.group_id, core_cluster_members.name AS member, grouped_services.info
  FROM grouped_services
  JOIN service_groups ON grouped_services.service_group_id = service_groups.id
  JOIN core_cluster_members ON grouped_services.member_id = core_cluster_members.id
  ORDER BY service_groups.id, service_groups.group_id, core_cluster_members.id
`)

var groupedServiceObjectsByMember = cluster.RegisterStmt(`
SELECT grouped_services.id, service_groups.service AS service, service_groups.group_id, core_cluster_members.name AS member, grouped_services.info
  FROM grouped_services
  JOIN service_groups ON grouped_services.service_group_id = service_groups.id
  JOIN core_cluster_members ON grouped_services.member_id = core_cluster_members.id
  WHERE ( member = ? )
  ORDER BY service_groups.id, service_groups.group_id, core_cluster_members.id
`)

var groupedServiceObjectsByServiceAndGroupID = cluster.RegisterStmt(`
SELECT grouped_services.id, service_groups.service AS service, service_groups.group_id, core_cluster_members.name AS member, grouped_services.info
  FROM grouped_services
  JOIN service_groups ON grouped_services.service_group_id = service_groups.id
  JOIN core_cluster_members ON grouped_services.member_id = core_cluster_members.id
  WHERE ( service = ? AND service_groups.group_id = ? )
  ORDER BY service_groups.id, service_groups.group_id, core_cluster_members.id
`)

var groupedServiceObjectsByMemberAndServiceAndGroupID = cluster.RegisterStmt(`
SELECT grouped_services.id, service_groups.service AS service, service_groups.group_id, core_cluster_members.name AS member, grouped_services.info
  FROM grouped_services
  JOIN service_groups ON grouped_services.service_group_id = service_groups.id
  JOIN core_cluster_members ON grouped_services.member_id = core_cluster_members.id
  WHERE ( member = ? AND service = ? AND service_groups.group_id = ? )
  ORDER BY service_groups.id, service_groups.group_id, core_cluster_members.id
`)

var groupedServiceID = cluster.RegisterStmt(`
SELECT grouped_services.id FROM grouped_services
  JOIN service_groups ON grouped_services.service_group_id = service_groups.id
  JOIN core_cluster_members ON grouped_services.member_id = core_cluster_members.id
  WHERE service_groups.service = ? AND service_groups.group_id = ? AND core_cluster_members.name = ?
`)

var groupedServiceCreate = cluster.RegisterStmt(`
INSERT INTO grouped_services (service_group_id, member_id, info)
  VALUES ((SELECT service_groups.id FROM service_groups WHERE service_groups.service = ? AND service_groups.group_id = ?), (SELECT core_cluster_members.id FROM core_cluster_members WHERE core_cluster_members.name = ?), ?)
`)

var groupedServiceDeleteByMember = cluster.RegisterStmt(`
DELETE FROM grouped_services WHERE member_id = (SELECT core_cluster_members.id FROM core_cluster_members WHERE core_cluster_members.name = ?)
`)

var groupedServiceDeleteByMemberAndServiceAndGroupID = cluster.RegisterStmt(`
DELETE FROM grouped_services WHERE member_id = (SELECT core_cluster_members.id FROM core_cluster_members WHERE core_cluster_members.name = ?) AND service_group_id = (SELECT service_groups.id FROM service_groups WHERE service_groups.service = ? AND service_groups.group_id = ?)
`)

var groupedServiceUpdate = cluster.RegisterStmt(`
UPDATE grouped_services
  SET service_group_id = (SELECT service_groups.id FROM service_groups WHERE service_groups.service = ? AND service_groups.group_id = ?), member_id = (SELECT core_cluster_members.id FROM core_cluster_members WHERE core_cluster_members.name = ?), info = ?
 WHERE id = ?
`)

// groupedServiceColumns 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 GroupedService entity.
func groupedServiceColumns() string {
	return "grouped_services.id, service_groups.service AS service, service_groups.group_id, core_cluster_members.name AS member, grouped_services.info"
}

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

	dest := func(scan func(dest ...any) error) error {
		g := GroupedService{}
		err := scan(&g.ID, &g.Service, &g.GroupID, &g.Member, &g.Info)
		if err != nil {
			return err
		}

		objects = append(objects, g)

		return nil
	}

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

	return objects, nil
}

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

	dest := func(scan func(dest ...any) error) error {
		g := GroupedService{}
		err := scan(&g.ID, &g.Service, &g.GroupID, &g.Member, &g.Info)
		if err != nil {
			return err
		}

		objects = append(objects, g)

		return nil
	}

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

	return objects, nil
}

// GetGroupedServices returns all available GroupedServices.
// generator: GroupedService GetMany
func GetGroupedServices(ctx context.Context, tx *sql.Tx, filters ...GroupedServiceFilter) ([]GroupedService, error) {
	var err error

	// Result slice.
	objects := make([]GroupedService, 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 = cluster.Stmt(tx, groupedServiceObjects)
		if err != nil {
			return nil, fmt.Errorf("Failed to get \"groupedServiceObjects\" prepared statement: %w", err)
		}
	}

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

				break
			}

			query, err := cluster.StmtString(groupedServiceObjectsByMemberAndServiceAndGroupID)
			if err != nil {
				return nil, fmt.Errorf("Failed to get \"groupedServiceObjects\" 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.Service != nil && filter.GroupID != nil && filter.Member == nil {
			args = append(args, []any{filter.Service, filter.GroupID}...)
			if len(filters) == 1 {
				sqlStmt, err = cluster.Stmt(tx, groupedServiceObjectsByServiceAndGroupID)
				if err != nil {
					return nil, fmt.Errorf("Failed to get \"groupedServiceObjectsByServiceAndGroupID\" prepared statement: %w", err)
				}

				break
			}

			query, err := cluster.StmtString(groupedServiceObjectsByServiceAndGroupID)
			if err != nil {
				return nil, fmt.Errorf("Failed to get \"groupedServiceObjects\" 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.Member != nil && filter.Service == nil && filter.GroupID == nil {
			args = append(args, []any{filter.Member}...)
			if len(filters) == 1 {
				sqlStmt, err = cluster.Stmt(tx, groupedServiceObjectsByMember)
				if err != nil {
					return nil, fmt.Errorf("Failed to get \"groupedServiceObjectsByMember\" prepared statement: %w", err)
				}

				break
			}

			query, err := cluster.StmtString(groupedServiceObjectsByMember)
			if err != nil {
				return nil, fmt.Errorf("Failed to get \"groupedServiceObjects\" 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.Service == nil && filter.GroupID == nil && filter.Member == nil {
			return nil, errors.New("Cannot filter on empty GroupedServiceFilter")
		} else {
			return nil, errors.New("No statement exists for the given Filter")
		}
	}

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

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

	return objects, nil
}

// GetGroupedService returns the GroupedService with the given key.
// generator: GroupedService GetOne
func GetGroupedService(ctx context.Context, tx *sql.Tx, service string, groupID string, member string) (*GroupedService, error) {
	filter := GroupedServiceFilter{}
	filter.Service = &service
	filter.GroupID = &groupID
	filter.Member = &member

	objects, err := GetGroupedServices(ctx, tx, filter)
	if err != nil {
		return nil, fmt.Errorf("Failed to fetch from \"grouped_services\" table: %w", err)
	}

	switch len(objects) {
	case 0:
		return nil, api.StatusErrorf(http.StatusNotFound, "GroupedService not found")
	case 1:
		return &objects[0], nil
	default:
		return nil, errors.New("More than one \"grouped_services\" entry matches")
	}
}

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

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

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

	return id, nil
}

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

		return false, err
	}

	return true, nil
}

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

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

	args := make([]any, 4)

	// Populate the statement arguments.
	args[0] = object.Service
	args[1] = object.GroupID
	args[2] = object.Member
	args[3] = object.Info

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

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

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

	return id, nil
}

// DeleteGroupedService deletes the GroupedService matching the given key parameters.
// generator: GroupedService DeleteOne-by-Member-and-Service-and-GroupID
func DeleteGroupedService(ctx context.Context, tx *sql.Tx, member string, service string, groupID string) error {
	stmt, err := cluster.Stmt(tx, groupedServiceDeleteByMemberAndServiceAndGroupID)
	if err != nil {
		return fmt.Errorf("Failed to get \"groupedServiceDeleteByMemberAndServiceAndGroupID\" prepared statement: %w", err)
	}

	result, err := stmt.Exec(member, service, groupID)
	if err != nil {
		return fmt.Errorf("Delete \"grouped_services\": %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, "GroupedService not found")
	} else if n > 1 {
		return fmt.Errorf("Query deleted %d GroupedService rows instead of 1", n)
	}

	return nil
}

// UpdateGroupedService updates the GroupedService matching the given key parameters.
// generator: GroupedService Update
func UpdateGroupedService(ctx context.Context, tx *sql.Tx, service string, groupID string, member string, object GroupedService) error {
	id, err := GetGroupedServiceID(ctx, tx, service, groupID, member)
	if err != nil {
		return err
	}

	stmt, err := cluster.Stmt(tx, groupedServiceUpdate)
	if err != nil {
		return fmt.Errorf("Failed to get \"groupedServiceUpdate\" prepared statement: %w", err)
	}

	result, err := stmt.Exec(object.Service, object.GroupID, object.Member, object.Info, id)
	if err != nil {
		return fmt.Errorf("Update \"grouped_services\" entry failed: %w", err)
	}

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

	if n != 1 {
		return fmt.Errorf("Query updated %d rows instead of 1", n)
	}

	return nil
}
