package assembler

import (
	"path/filepath"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/kits/agentteam-insight/internal/graphbuilder/schema"
)

func MergeGraphUpdate(existing schema.KnowledgeGraph, fresh schema.KnowledgeGraph, changedFilePaths []string, gitCommitHash string, now time.Time) schema.KnowledgeGraph {
	pruned := pruneGraphForChangedFiles(existing, changedFilePaths, gitCommitHash, now, false)
	merged := pruned
	merged.Nodes = append(merged.Nodes, fresh.Nodes...)
	merged.Edges = append(merged.Edges, fresh.Edges...)
	merged = NormalizeGraph(merged)
	return merged
}

func PruneGraphForChangedFiles(existing schema.KnowledgeGraph, changedFilePaths []string, gitCommitHash string, now time.Time) schema.KnowledgeGraph {
	return pruneGraphForChangedFiles(existing, changedFilePaths, gitCommitHash, now, true)
}

func pruneGraphForChangedFiles(existing schema.KnowledgeGraph, changedFilePaths []string, gitCommitHash string, now time.Time, normalize bool) schema.KnowledgeGraph {
	changed := normalizePathSet(changedFilePaths)
	retainedNodes := make([]schema.GraphNode, 0, len(existing.Nodes))
	removedNodeIDs := map[string]struct{}{}
	for _, node := range existing.Nodes {
		if _, ok := changed[cleanPath(node.FilePath)]; ok {
			removedNodeIDs[node.ID] = struct{}{}
			continue
		}
		retainedNodes = append(retainedNodes, node)
	}
	retainedIDSet := map[string]struct{}{}
	for _, node := range retainedNodes {
		retainedIDSet[node.ID] = struct{}{}
	}
	retainedEdges := make([]schema.GraphEdge, 0, len(existing.Edges))
	for _, edge := range existing.Edges {
		if _, removed := removedNodeIDs[edge.Source]; removed {
			continue
		}
		if _, removed := removedNodeIDs[edge.Target]; removed {
			continue
		}
		if _, ok := retainedIDSet[edge.Source]; !ok {
			continue
		}
		if _, ok := retainedIDSet[edge.Target]; !ok {
			continue
		}
		retainedEdges = append(retainedEdges, edge)
	}
	pruned := existing
	pruned.Nodes = retainedNodes
	pruned.Edges = retainedEdges
	if gitCommitHash != "" {
		pruned.Project.GitCommitHash = gitCommitHash
	}
	if !now.IsZero() {
		pruned.Project.AnalyzedAt = now.UTC().Format(time.RFC3339)
	}
	if normalize {
		return NormalizeGraph(pruned)
	}
	return pruned
}

func normalizePathSet(paths []string) map[string]struct{} {
	set := map[string]struct{}{}
	for _, path := range paths {
		path = cleanPath(path)
		if path != "" {
			set[path] = struct{}{}
		}
	}
	return set
}

func cleanPath(path string) string {
	path = strings.TrimSpace(path)
	if path == "" {
		return ""
	}
	path = filepath.ToSlash(filepath.Clean(path))
	path = strings.TrimPrefix(path, "./")
	if path == "." {
		return ""
	}
	return path
}
