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

// Code generated by generate-database from the incus project - DO NOT EDIT.

package cluster

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"strings"
)

var networkACLObjects = RegisterStmt(`
SELECT networks_acls.id, networks_acls.project_id, projects.name AS project, networks_acls.name, networks_acls.description, networks_acls.ingress, networks_acls.egress
  FROM networks_acls
  JOIN projects ON networks_acls.project_id = projects.id
  ORDER BY networks_acls.id
`)

var networkACLObjectsByID = RegisterStmt(`
SELECT networks_acls.id, networks_acls.project_id, projects.name AS project, networks_acls.name, networks_acls.description, networks_acls.ingress, networks_acls.egress
  FROM networks_acls
  JOIN projects ON networks_acls.project_id = projects.id
  WHERE ( networks_acls.id = ? )
  ORDER BY networks_acls.id
`)

var networkACLObjectsByName = RegisterStmt(`
SELECT networks_acls.id, networks_acls.project_id, projects.name AS project, networks_acls.name, networks_acls.description, networks_acls.ingress, networks_acls.egress
  FROM networks_acls
  JOIN projects ON networks_acls.project_id = projects.id
  WHERE ( networks_acls.name = ? )
  ORDER BY networks_acls.id
`)

var networkACLObjectsByProject = RegisterStmt(`
SELECT networks_acls.id, networks_acls.project_id, projects.name AS project, networks_acls.name, networks_acls.description, networks_acls.ingress, networks_acls.egress
  FROM networks_acls
  JOIN projects ON networks_acls.project_id = projects.id
  WHERE ( project = ? )
  ORDER BY networks_acls.id
`)

var networkACLObjectsByProjectAndName = RegisterStmt(`
SELECT networks_acls.id, networks_acls.project_id, projects.name AS project, networks_acls.name, networks_acls.description, networks_acls.ingress, networks_acls.egress
  FROM networks_acls
  JOIN projects ON networks_acls.project_id = projects.id
  WHERE ( project = ? AND networks_acls.name = ? )
  ORDER BY networks_acls.id
`)

var networkACLID = RegisterStmt(`
SELECT networks_acls.id FROM networks_acls
  JOIN projects ON networks_acls.project_id = projects.id
  WHERE projects.name = ? AND networks_acls.name = ?
`)

var networkACLCreate = RegisterStmt(`
INSERT INTO networks_acls (project_id, name, description, ingress, egress)
  VALUES ((SELECT projects.id FROM projects WHERE projects.name = ?), ?, ?, ?, ?)
`)

var networkACLRename = RegisterStmt(`
UPDATE networks_acls SET name = ? WHERE project_id = (SELECT projects.id FROM projects WHERE projects.name = ?) AND name = ?
`)

var networkACLUpdate = RegisterStmt(`
UPDATE networks_acls
  SET project_id = (SELECT projects.id FROM projects WHERE projects.name = ?), name = ?, description = ?, ingress = ?, egress = ?
 WHERE id = ?
`)

var networkACLDeleteByID = RegisterStmt(`
DELETE FROM networks_acls WHERE id = ?
`)

// networkACLColumns 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 NetworkACL entity.
func networkACLColumns() string {
	return "networks_acls.id, networks_acls.project_id, projects.name AS project, networks_acls.name, networks_acls.description, networks_acls.ingress, networks_acls.egress"
}

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

	dest := func(scan func(dest ...any) error) error {
		n := NetworkACL{}
		var ingressStr string
		var egressStr string
		err := scan(&n.ID, &n.ProjectID, &n.Project, &n.Name, &n.Description, &ingressStr, &egressStr)
		if err != nil {
			return err
		}

		err = unmarshalJSON(ingressStr, &n.Ingress)
		if err != nil {
			return err
		}

		err = unmarshalJSON(egressStr, &n.Egress)
		if err != nil {
			return err
		}

		objects = append(objects, n)

		return nil
	}

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

	return objects, nil
}

// getNetworkACLsRaw can be used to run handwritten query strings to return a slice of objects.
func getNetworkACLsRaw(ctx context.Context, db dbtx, sql string, args ...any) ([]NetworkACL, error) {
	objects := make([]NetworkACL, 0)

	dest := func(scan func(dest ...any) error) error {
		n := NetworkACL{}
		var ingressStr string
		var egressStr string
		err := scan(&n.ID, &n.ProjectID, &n.Project, &n.Name, &n.Description, &ingressStr, &egressStr)
		if err != nil {
			return err
		}

		err = unmarshalJSON(ingressStr, &n.Ingress)
		if err != nil {
			return err
		}

		err = unmarshalJSON(egressStr, &n.Egress)
		if err != nil {
			return err
		}

		objects = append(objects, n)

		return nil
	}

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

	return objects, nil
}

// GetNetworkACLs returns all available NetworkACLs.
// generator: NetworkACL GetMany
func GetNetworkACLs(ctx context.Context, db dbtx, filters ...NetworkACLFilter) (_ []NetworkACL, _err error) {
	defer func() {
		_err = mapErr(_err, "NetworkACL")
	}()

	var err error

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

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

				break
			}

			query, err := StmtString(networkACLObjectsByProjectAndName)
			if err != nil {
				return nil, fmt.Errorf("Failed to get \"networkACLObjects\" 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.Project != nil && filter.ID == nil && filter.Name == nil {
			args = append(args, []any{filter.Project}...)
			if len(filters) == 1 {
				sqlStmt, err = Stmt(db, networkACLObjectsByProject)
				if err != nil {
					return nil, fmt.Errorf("Failed to get \"networkACLObjectsByProject\" prepared statement: %w", err)
				}

				break
			}

			query, err := StmtString(networkACLObjectsByProject)
			if err != nil {
				return nil, fmt.Errorf("Failed to get \"networkACLObjects\" 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.Name != nil && filter.ID == nil && filter.Project == nil {
			args = append(args, []any{filter.Name}...)
			if len(filters) == 1 {
				sqlStmt, err = Stmt(db, networkACLObjectsByName)
				if err != nil {
					return nil, fmt.Errorf("Failed to get \"networkACLObjectsByName\" prepared statement: %w", err)
				}

				break
			}

			query, err := StmtString(networkACLObjectsByName)
			if err != nil {
				return nil, fmt.Errorf("Failed to get \"networkACLObjects\" 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.ID != nil && filter.Name == nil && filter.Project == nil {
			args = append(args, []any{filter.ID}...)
			if len(filters) == 1 {
				sqlStmt, err = Stmt(db, networkACLObjectsByID)
				if err != nil {
					return nil, fmt.Errorf("Failed to get \"networkACLObjectsByID\" prepared statement: %w", err)
				}

				break
			}

			query, err := StmtString(networkACLObjectsByID)
			if err != nil {
				return nil, fmt.Errorf("Failed to get \"networkACLObjects\" 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.ID == nil && filter.Name == nil && filter.Project == nil {
			return nil, fmt.Errorf("Cannot filter on empty NetworkACLFilter")
		} else {
			return nil, errors.New("No statement exists for the given Filter")
		}
	}

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

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

	return objects, nil
}

// GetNetworkACLConfig returns all available NetworkACL Config
// generator: NetworkACL GetMany
func GetNetworkACLConfig(ctx context.Context, db tx, networkACLID int, filters ...ConfigFilter) (_ map[string]string, _err error) {
	defer func() {
		_err = mapErr(_err, "NetworkACL")
	}()

	if len(filters) == 0 {
		filters = append(filters, ConfigFilter{})
	}

	for i := range filters {
		filters[i].ReferenceID = []int{networkACLID}
	}

	networkACLConfig, err := GetConfig(ctx, db, "networks_acls", "network_acl", filters...)
	if err != nil {
		return nil, err
	}

	config, ok := networkACLConfig[networkACLID]
	if !ok {
		config = map[string]string{}
	}

	return config, nil
}

// GetNetworkACL returns the NetworkACL with the given key.
// generator: NetworkACL GetOne
func GetNetworkACL(ctx context.Context, db dbtx, project string, name string) (_ *NetworkACL, _err error) {
	defer func() {
		_err = mapErr(_err, "NetworkACL")
	}()

	filter := NetworkACLFilter{}
	filter.Project = &project
	filter.Name = &name

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

	switch len(objects) {
	case 0:
		return nil, ErrNotFound
	case 1:
		return &objects[0], nil
	default:
		return nil, fmt.Errorf("More than one \"networks_acls\" entry matches")
	}
}

// NetworkACLExists checks if a NetworkACL with the given key exists.
// generator: NetworkACL Exists
func NetworkACLExists(ctx context.Context, db dbtx, project string, name string) (_ bool, _err error) {
	defer func() {
		_err = mapErr(_err, "NetworkACL")
	}()

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

	row := stmt.QueryRowContext(ctx, project, name)
	var id int64
	err = row.Scan(&id)
	if errors.Is(err, sql.ErrNoRows) {
		return false, nil
	}

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

	return true, nil
}

// CreateNetworkACL adds a new NetworkACL to the database.
// generator: NetworkACL Create
func CreateNetworkACL(ctx context.Context, db dbtx, object NetworkACL) (_ int64, _err error) {
	defer func() {
		_err = mapErr(_err, "NetworkACL")
	}()

	args := make([]any, 5)

	// Populate the statement arguments.
	args[0] = object.Project
	args[1] = object.Name
	args[2] = object.Description
	marshaledIngress, err := marshalJSON(object.Ingress)
	if err != nil {
		return -1, err
	}

	args[3] = marshaledIngress
	marshaledEgress, err := marshalJSON(object.Egress)
	if err != nil {
		return -1, err
	}

	args[4] = marshaledEgress

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

	// Execute the statement.
	result, err := stmt.Exec(args...)
	if err != nil && strings.HasPrefix(err.Error(), "UNIQUE constraint failed:") {
		return -1, ErrConflict
	}

	if err != nil {
		return -1, fmt.Errorf("Failed to create \"networks_acls\" entry: %w", err)
	}

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

	return id, nil
}

// CreateNetworkACLConfig adds new NetworkACL Config to the database.
// generator: NetworkACL Create
func CreateNetworkACLConfig(ctx context.Context, db dbtx, networkACLID int64, config map[string]string) (_err error) {
	defer func() {
		_err = mapErr(_err, "NetworkACL")
	}()

	referenceID := int(networkACLID)
	for key, value := range config {
		insert := Config{
			ReferenceID: referenceID,
			Key:         key,
			Value:       value,
		}

		err := CreateConfig(ctx, db, "networks_acls", "network_acl", insert)
		if err != nil {
			return fmt.Errorf("Insert Config failed for NetworkACL: %w", err)
		}

	}

	return nil
}

// GetNetworkACLID return the ID of the NetworkACL with the given key.
// generator: NetworkACL ID
func GetNetworkACLID(ctx context.Context, db tx, project string, name string) (_ int64, _err error) {
	defer func() {
		_err = mapErr(_err, "NetworkACL")
	}()

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

	row := stmt.QueryRowContext(ctx, project, name)
	var id int64
	err = row.Scan(&id)
	if errors.Is(err, sql.ErrNoRows) {
		return -1, ErrNotFound
	}

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

	return id, nil
}

// RenameNetworkACL renames the NetworkACL matching the given key parameters.
// generator: NetworkACL Rename
func RenameNetworkACL(ctx context.Context, db dbtx, project string, name string, to string) (_err error) {
	defer func() {
		_err = mapErr(_err, "NetworkACL")
	}()

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

	result, err := stmt.Exec(to, project, name)
	if err != nil {
		return fmt.Errorf("Rename NetworkACL failed: %w", err)
	}

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

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

	return nil
}

// UpdateNetworkACL updates the NetworkACL matching the given key parameters.
// generator: NetworkACL Update
func UpdateNetworkACL(ctx context.Context, db tx, project string, name string, object NetworkACL) (_err error) {
	defer func() {
		_err = mapErr(_err, "NetworkACL")
	}()

	id, err := GetNetworkACLID(ctx, db, project, name)
	if err != nil {
		return err
	}

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

	marshaledIngress, err := marshalJSON(object.Ingress)
	if err != nil {
		return err
	}

	marshaledEgress, err := marshalJSON(object.Egress)
	if err != nil {
		return err
	}

	result, err := stmt.Exec(object.Project, object.Name, object.Description, marshaledIngress, marshaledEgress, id)
	if err != nil {
		return fmt.Errorf("Update \"networks_acls\" 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
}

// UpdateNetworkACLConfig updates the NetworkACL Config matching the given key parameters.
// generator: NetworkACL Update
func UpdateNetworkACLConfig(ctx context.Context, db tx, networkACLID int64, config map[string]string) (_err error) {
	defer func() {
		_err = mapErr(_err, "NetworkACL")
	}()

	err := UpdateConfig(ctx, db, "networks_acls", "network_acl", int(networkACLID), config)
	if err != nil {
		return fmt.Errorf("Replace Config for NetworkACL failed: %w", err)
	}

	return nil
}

// DeleteNetworkACL deletes the NetworkACL matching the given key parameters.
// generator: NetworkACL DeleteOne-by-ID
func DeleteNetworkACL(ctx context.Context, db dbtx, id int) (_err error) {
	defer func() {
		_err = mapErr(_err, "NetworkACL")
	}()

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

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

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

	if n == 0 {
		return ErrNotFound
	} else if n > 1 {
		return fmt.Errorf("Query deleted %d NetworkACL rows instead of 1", n)
	}

	return nil
}
