package managedlifecycle

import (
	"context"
	"database/sql"
	"fmt"
	"sort"
	"strings"

	cp "github.com/pingcap-inc/nexuscrm-contract/pkg/controlplane"
)

type componentSQLExecutor interface {
	QueryContext(context.Context, string, ...any) (*sql.Rows, error)
	ExecContext(context.Context, string, ...any) (sql.Result, error)
}

func validateComponentSQLIdentifier(identifier string) error {
	if identifier == "" || strings.TrimSpace(identifier) != identifier {
		return fmt.Errorf("invalid SQL identifier %q", identifier)
	}
	for i := 0; i < len(identifier); i++ {
		character := identifier[i]
		if (character >= 'a' && character <= 'z') ||
			(character >= 'A' && character <= 'Z') || character == '_' ||
			(i > 0 && character >= '0' && character <= '9') {
			continue
		}
		return fmt.Errorf("invalid SQL identifier %q", identifier)
	}
	return nil
}

func sortedComponentRowKeys(row cp.ComponentRow) []string {
	keys := make([]string, 0, len(row))
	for key := range row {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	return keys
}

func componentPredicates(row cp.ComponentRow, columns []string) (string, []any) {
	parts := make([]string, 0, len(columns))
	args := make([]any, 0, len(columns))
	for _, column := range columns {
		if row[column] == nil {
			parts = append(parts, quoteComponentIdentifier(column)+" IS NULL")
			continue
		}
		parts = append(parts, quoteComponentIdentifier(column)+" = ?")
		args = append(args, row[column])
	}
	return strings.Join(parts, " AND "), args
}

func componentMutationExpressions(
	values cp.ComponentRow,
	valueColumns, timestampColumns []string,
	assignments bool,
) ([]string, []any) {
	parts := make([]string, 0, len(valueColumns)+len(timestampColumns))
	args := make([]any, 0, len(valueColumns))
	for _, column := range valueColumns {
		expression := "?"
		if assignments {
			expression = quoteComponentIdentifier(column) + " = ?"
		}
		parts = append(parts, expression)
		args = append(args, values[column])
	}
	for _, column := range timestampColumns {
		expression := "CURRENT_TIMESTAMP"
		if assignments {
			expression = quoteComponentIdentifier(column) + " = CURRENT_TIMESTAMP"
		}
		parts = append(parts, expression)
	}
	return parts, args
}

func quoteComponentIdentifier(identifier string) string { return "`" + identifier + "`" }

func quoteComponentIdentifiers(identifiers []string) string {
	quoted := make([]string, len(identifiers))
	for index, identifier := range identifiers {
		quoted[index] = quoteComponentIdentifier(identifier)
	}
	return strings.Join(quoted, ", ")
}

func componentRowsAffected(result sql.Result, err error) (int64, error) {
	if err != nil {
		return 0, err
	}
	return result.RowsAffected()
}

func canonicalManagedComponentKeys(keys []string) ([]string, error) {
	out := append([]string(nil), keys...)
	for index, key := range out {
		if strings.TrimSpace(key) == "" || strings.TrimSpace(key) != key {
			return nil, fmt.Errorf("managed component key %d is not canonical", index)
		}
	}
	sort.Strings(out)
	for index := 1; index < len(out); index++ {
		if out[index-1] == out[index] {
			return nil, fmt.Errorf("managed component key %q is duplicated", out[index])
		}
	}
	return out, nil
}
