package pg

import (
	"context"
	"encoding/json"
	"time"

	"github.com/lib/pq"
	"github.com/nextlevelbuilder/goclaw/internal/store"
)

// Traverse walks the knowledge graph from startEntityID up to maxDepth hops
// using a recursive CTE. Returns all reachable entities (excluding the start node).
// A 5-second statement timeout is applied for safety.
func (s *PGKnowledgeGraphStore) Traverse(ctx context.Context, agentID, userID, startEntityID string, maxDepth int) ([]store.TraversalResult, error) {
	if maxDepth <= 0 {
		maxDepth = 3
	}

	aid := mustParseUUID(agentID)
	startID := mustParseUUID(startEntityID)

	tx, err := s.db.BeginTx(ctx, nil)
	if err != nil {
		return nil, err
	}
	defer tx.Rollback() //nolint:errcheck

	if _, err := tx.ExecContext(ctx, `SET LOCAL statement_timeout = '5000'`); err != nil {
		return nil, err
	}

	rows, err := tx.QueryContext(ctx, `
		WITH RECURSIVE paths AS (
			SELECT
				e.id, e.agent_id, e.user_id, e.external_id,
				e.name, e.entity_type, e.description,
				e.properties, e.source_id, e.confidence,
				e.created_at, e.updated_at,
				1 AS depth,
				ARRAY[e.id::text] AS path,
				''::text AS via
			FROM kg_entities e
			WHERE e.id = $1 AND e.agent_id = $2 AND e.user_id = $3

			UNION ALL

			SELECT
				e.id, e.agent_id, e.user_id, e.external_id,
				e.name, e.entity_type, e.description,
				e.properties, e.source_id, e.confidence,
				e.created_at, e.updated_at,
				p.depth + 1,
				p.path || e.id::text,
				r.relation_type
			FROM paths p
			JOIN kg_relations r ON p.id = r.source_entity_id AND r.user_id = $3
			JOIN kg_entities  e ON r.target_entity_id = e.id AND e.user_id = $3
			WHERE p.depth < $4
			  AND NOT e.id::text = ANY(p.path)
		)
		SELECT
			id, agent_id, user_id, external_id,
			name, entity_type, description,
			properties, source_id, confidence,
			created_at, updated_at,
			depth, path, via
		FROM paths WHERE depth > 1`,
		startID, aid, userID, maxDepth,
	)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var results []store.TraversalResult
	for rows.Next() {
		var e store.Entity
		var props []byte
		var createdAt, updatedAt time.Time
		var depth int
		var path []string
		var via string

		if err := rows.Scan(
			&e.ID, &e.AgentID, &e.UserID, &e.ExternalID,
			&e.Name, &e.EntityType, &e.Description,
			&props, &e.SourceID, &e.Confidence,
			&createdAt, &updatedAt,
			&depth, pq.Array(&path), &via,
		); err != nil {
			continue
		}
		if len(props) > 0 {
			json.Unmarshal(props, &e.Properties) //nolint:errcheck
		}
		e.CreatedAt = createdAt.UnixMilli()
		e.UpdatedAt = updatedAt.UnixMilli()

		results = append(results, store.TraversalResult{
			Entity: e,
			Depth:  depth,
			Path:   path,
			Via:    via,
		})
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}

	return results, tx.Commit()
}
