package core

import (
	"database/sql"
	"fmt"
)

// DetectCycle checks if adding a directed edge (from -> to) would create a cycle
// in a directed graph stored in a SQLite table.
// It performs BFS from toNode following edges where the filter column is non-empty.
// If it reaches fromNode, a cycle exists.
//
// Parameters:
//   - conn: open *sql.DB connection
//   - table: table name containing the edges (e.g. "relations")
//   - fromCol: column name for edge source (e.g. "from_entity")
//   - toCol: column name for edge destination (e.g. "to_entity")
//   - filterCol: column name that must be non-empty for causal edges (e.g. "via"); pass "" to consider all edges
//   - fromNode: source node of the proposed new edge
//   - toNode: destination node of the proposed new edge
func DetectCycle(conn *sql.DB, table, fromCol, toCol, filterCol, fromNode, toNode string) error {
	if fromNode == "" || toNode == "" {
		return nil
	}

	var query string
	if filterCol != "" {
		query = fmt.Sprintf("SELECT %s FROM %s WHERE %s = ? AND %s != ''", toCol, table, fromCol, filterCol)
	} else {
		query = fmt.Sprintf("SELECT %s FROM %s WHERE %s = ?", toCol, table, fromCol)
	}

	visited := map[string]bool{}
	queue := []string{toNode}

	for len(queue) > 0 {
		current := queue[0]
		queue = queue[1:]

		if visited[current] {
			continue
		}
		visited[current] = true

		if current == fromNode {
			return fmt.Errorf("cycle detected: adding edge %s -> %s would create a cycle", fromNode, toNode)
		}

		rows, err := conn.Query(query, current)
		if err != nil {
			return fmt.Errorf("querying %s for cycle detection: %w", table, err)
		}

		for rows.Next() {
			var next string
			if err := rows.Scan(&next); err != nil {
				rows.Close()
				return err
			}
			if !visited[next] {
				queue = append(queue, next)
			}
		}
		rows.Close()
	}

	return nil
}
