package coordination

import (
	"context"
	"fmt"
	"strings"
	"time"

	"chorus.services/bzzz/pubsub"
	"github.com/libp2p/go-libp2p/core/peer"
)

// DependencyDetector analyzes tasks across repositories for relationships
type DependencyDetector struct {
	pubsub            *pubsub.PubSub
	ctx               context.Context
	knownTasks        map[string]*TaskContext // taskKey -> context
	dependencyRules   []DependencyRule
	coordinationHops  int
}

// TaskContext represents a task with its repository and project context
type TaskContext struct {
	TaskID      int    `json:"task_id"`
	ProjectID   int    `json:"project_id"`
	Repository  string `json:"repository"`
	Title       string `json:"title"`
	Description string `json:"description"`
	Keywords    []string `json:"keywords"`
	AgentID     string `json:"agent_id"`
	ClaimedAt   time.Time `json:"claimed_at"`
}

// DependencyRule defines how to detect task relationships
type DependencyRule struct {
	Name        string
	Description string
	Keywords    []string
	Validator   func(task1, task2 *TaskContext) (bool, string)
}

// TaskDependency represents a detected relationship between tasks
type TaskDependency struct {
	Task1       *TaskContext `json:"task1"`
	Task2       *TaskContext `json:"task2"`
	Relationship string       `json:"relationship"`
	Confidence   float64      `json:"confidence"`
	Reason       string       `json:"reason"`
	DetectedAt   time.Time    `json:"detected_at"`
}

// NewDependencyDetector creates a new cross-repository dependency detector
func NewDependencyDetector(ctx context.Context, ps *pubsub.PubSub) *DependencyDetector {
	dd := &DependencyDetector{
		pubsub:           ps,
		ctx:              ctx,
		knownTasks:       make(map[string]*TaskContext),
		coordinationHops: 3, // Limit meta discussion depth
	}
	
	// Initialize common dependency detection rules
	dd.initializeDependencyRules()
	
	// Subscribe to task announcements for dependency detection
	go dd.listenForTaskAnnouncements()
	
	return dd
}

// initializeDependencyRules sets up common patterns for task relationships
func (dd *DependencyDetector) initializeDependencyRules() {
	dd.dependencyRules = []DependencyRule{
		{
			Name:        "API_Contract",
			Description: "Tasks involving API contracts and implementations",
			Keywords:    []string{"api", "endpoint", "contract", "interface", "schema"},
			Validator: func(task1, task2 *TaskContext) (bool, string) {
				// Check if one task defines API and another implements it
				text1 := strings.ToLower(task1.Title + " " + task1.Description)
				text2 := strings.ToLower(task2.Title + " " + task2.Description)
				
				if (strings.Contains(text1, "api") && strings.Contains(text2, "implement")) ||
				   (strings.Contains(text2, "api") && strings.Contains(text1, "implement")) {
					return true, "API definition and implementation dependency"
				}
				return false, ""
			},
		},
		{
			Name:        "Database_Schema",
			Description: "Database schema changes affecting multiple services",
			Keywords:    []string{"database", "schema", "migration", "table", "model"},
			Validator: func(task1, task2 *TaskContext) (bool, string) {
				text1 := strings.ToLower(task1.Title + " " + task1.Description)
				text2 := strings.ToLower(task2.Title + " " + task2.Description)
				
				dbKeywords := []string{"database", "schema", "migration", "table"}
				hasDB1 := false
				hasDB2 := false
				
				for _, keyword := range dbKeywords {
					if strings.Contains(text1, keyword) { hasDB1 = true }
					if strings.Contains(text2, keyword) { hasDB2 = true }
				}
				
				if hasDB1 && hasDB2 {
					return true, "Database schema dependency detected"
				}
				return false, ""
			},
		},
		{
			Name:        "Configuration_Dependency",
			Description: "Configuration changes affecting multiple components",
			Keywords:    []string{"config", "environment", "settings", "parameters"},
			Validator: func(task1, task2 *TaskContext) (bool, string) {
				text1 := strings.ToLower(task1.Title + " " + task1.Description)
				text2 := strings.ToLower(task2.Title + " " + task2.Description)
				
				if (strings.Contains(text1, "config") || strings.Contains(text1, "environment")) &&
				   (strings.Contains(text2, "config") || strings.Contains(text2, "environment")) {
					return true, "Configuration dependency - coordinated changes needed"
				}
				return false, ""
			},
		},
		{
			Name:        "Security_Compliance",
			Description: "Security changes requiring coordinated implementation",
			Keywords:    []string{"security", "auth", "permission", "token", "encrypt"},
			Validator: func(task1, task2 *TaskContext) (bool, string) {
				text1 := strings.ToLower(task1.Title + " " + task1.Description)
				text2 := strings.ToLower(task2.Title + " " + task2.Description)
				
				secKeywords := []string{"security", "auth", "permission", "token"}
				hasSecu1 := false
				hasSecu2 := false
				
				for _, keyword := range secKeywords {
					if strings.Contains(text1, keyword) { hasSecu1 = true }
					if strings.Contains(text2, keyword) { hasSecu2 = true }
				}
				
				if hasSecu1 && hasSecu2 {
					return true, "Security implementation requires coordination"
				}
				return false, ""
			},
		},
	}
}

// RegisterTask adds a task to the dependency tracking system
func (dd *DependencyDetector) RegisterTask(task *TaskContext) {
	taskKey := fmt.Sprintf("%d:%d", task.ProjectID, task.TaskID)
	dd.knownTasks[taskKey] = task
	
	fmt.Printf("🔍 Registered task for dependency detection: %s/%s #%d\n", 
		task.Repository, task.Title, task.TaskID)
	
	// Check for dependencies with existing tasks
	dd.detectDependencies(task)
}

// detectDependencies analyzes a new task against existing tasks for relationships
func (dd *DependencyDetector) detectDependencies(newTask *TaskContext) {
	for _, existingTask := range dd.knownTasks {
		// Skip self-comparison
		if existingTask.TaskID == newTask.TaskID && existingTask.ProjectID == newTask.ProjectID {
			continue
		}
		
		// Skip if same repository (handled by single-repo coordination)
		if existingTask.Repository == newTask.Repository {
			continue
		}
		
		// Apply dependency detection rules
		for _, rule := range dd.dependencyRules {
			if matches, reason := rule.Validator(newTask, existingTask); matches {
				dependency := &TaskDependency{
					Task1:        newTask,
					Task2:        existingTask,
					Relationship: rule.Name,
					Confidence:   0.8, // Could be improved with ML
					Reason:       reason,
					DetectedAt:   time.Now(),
				}
				
				dd.announceDependency(dependency)
			}
		}
	}
}

// announceDependency broadcasts a detected dependency for agent coordination
func (dd *DependencyDetector) announceDependency(dep *TaskDependency) {
	fmt.Printf("🔗 Dependency detected: %s/%s #%d ↔ %s/%s #%d (%s)\n",
		dep.Task1.Repository, dep.Task1.Title, dep.Task1.TaskID,
		dep.Task2.Repository, dep.Task2.Title, dep.Task2.TaskID,
		dep.Relationship)
	
	// Create coordination message for HMMM meta-discussion
	coordMsg := map[string]interface{}{
		"message_type":   "dependency_detected",
		"dependency":     dep,
		"coordination_request": fmt.Sprintf(
			"Cross-repository dependency detected between tasks. "+
			"Agent working on %s/%s #%d and agent working on %s/%s #%d should coordinate. "+
			"Relationship: %s. Reason: %s",
			dep.Task1.Repository, dep.Task1.Title, dep.Task1.TaskID,
			dep.Task2.Repository, dep.Task2.Title, dep.Task2.TaskID,
			dep.Relationship, dep.Reason,
		),
		"agents_involved": []string{dep.Task1.AgentID, dep.Task2.AgentID},
		"repositories":    []string{dep.Task1.Repository, dep.Task2.Repository},
		"hop_count":       0,
		"max_hops":        dd.coordinationHops,
		"detected_at":     dep.DetectedAt.Unix(),
	}
	
	// Publish to HMMM meta-discussion channel
	if err := dd.pubsub.PublishHmmmMessage(pubsub.MetaDiscussion, coordMsg); err != nil {
		fmt.Printf("❌ Failed to announce dependency: %v\n", err)
	} else {
		fmt.Printf("📡 Dependency coordination request sent to HMMM channel\n")
	}
}

// listenForTaskAnnouncements monitors the P2P mesh for task claims
func (dd *DependencyDetector) listenForTaskAnnouncements() {
	// This would integrate with the existing pubsub system
	// to automatically detect when agents claim tasks
	fmt.Printf("👂 Dependency detector listening for task announcements...\n")
	
	// In a real implementation, this would subscribe to TaskClaim messages
	// and extract task context for dependency analysis
}

// GetKnownTasks returns all tasks currently being tracked
func (dd *DependencyDetector) GetKnownTasks() map[string]*TaskContext {
	return dd.knownTasks
}

// GetDependencyRules returns the configured dependency detection rules
func (dd *DependencyDetector) GetDependencyRules() []DependencyRule {
	return dd.dependencyRules
}

// AddCustomRule allows adding project-specific dependency detection
func (dd *DependencyDetector) AddCustomRule(rule DependencyRule) {
	dd.dependencyRules = append(dd.dependencyRules, rule)
	fmt.Printf("➕ Added custom dependency rule: %s\n", rule.Name)
}