package github

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/deepblackcloud/bzzz/pubsub"
	"github.com/deepblackcloud/bzzz/reasoning"
	"github.com/libp2p/go-libp2p/core/peer"
)

const (
	// humanEscalationWebhookURL is the N8N webhook for escalating tasks.
	humanEscalationWebhookURL = "https://n8n.home.deepblack.cloud/webhook-test/human-escalation"
	// conversationHistoryLimit is the number of messages before auto-escalation.
	conversationHistoryLimit = 10
)

var escalationKeywords = []string{"stuck", "help", "human", "escalate", "clarification needed", "manual intervention"}

// Conversation represents the history of a discussion for a task.
type Conversation struct {
	TaskID          int
	TaskTitle       string
	TaskDescription string
	History         []string
	LastUpdated     time.Time
	IsEscalated     bool
}

// Integration handles the integration between GitHub tasks and Bzzz P2P coordination
type Integration struct {
	client *Client
	pubsub *pubsub.PubSub
	ctx    context.Context
	config *IntegrationConfig

	activeDiscussions map[int]*Conversation
	discussionLock    sync.RWMutex
}

// IntegrationConfig holds configuration for GitHub-Bzzz integration
type IntegrationConfig struct {
	PollInterval time.Duration
	MaxTasks     int
	AgentID      string
	Capabilities []string
}

// NewIntegration creates a new GitHub-Bzzz integration
func NewIntegration(ctx context.Context, client *Client, ps *pubsub.PubSub, config *IntegrationConfig) *Integration {
	if config.PollInterval == 0 {
		config.PollInterval = 30 * time.Second
	}
	if config.MaxTasks == 0 {
		config.MaxTasks = 3
	}

	return &Integration{
		client:            client,
		pubsub:            ps,
		ctx:               ctx,
		config:            config,
		activeDiscussions: make(map[int]*Conversation),
	}
}

// Start begins the GitHub-Bzzz integration
func (i *Integration) Start() {
	fmt.Printf("🔗 Starting GitHub-Bzzz integration for agent: %s\n", i.config.AgentID)
	i.pubsub.SetAntennaeMessageHandler(i.handleMetaDiscussion)
	go i.pollForTasks()
}

// pollForTasks periodically checks GitHub for available tasks
func (i *Integration) pollForTasks() {
	ticker := time.NewTicker(i.config.PollInterval)
	defer ticker.Stop()

	for {
		select {
		case <-i.ctx.Done():
			return
		case <-ticker.C:
			if err := i.checkAndClaimTasks(); err != nil {
				fmt.Printf("❌ Error checking tasks: %v\n", err)
			}
		}
	}
}

// checkAndClaimTasks looks for available tasks and claims suitable ones
func (i *Integration) checkAndClaimTasks() error {
	tasks, err := i.client.ListAvailableTasks()
	if err != nil {
		return fmt.Errorf("failed to list tasks: %w", err)
	}
	if len(tasks) == 0 {
		return nil
	}

	suitableTasks := i.filterSuitableTasks(tasks)
	if len(suitableTasks) == 0 {
		return nil
	}

	task := suitableTasks[0]
	claimedTask, err := i.client.ClaimTask(task.Number, i.config.AgentID)
	if err != nil {
		return fmt.Errorf("failed to claim task %d: %w", task.Number, err)
	}
	fmt.Printf("✋ Claimed task #%d: %s\n", claimedTask.Number, claimedTask.Title)

	go i.executeTask(claimedTask)
	return nil
}

// executeTask starts the task by generating and proposing a plan.
func (i *Integration) executeTask(task *Task) {
	fmt.Printf("🚀 Starting execution of task #%d: %s\n", task.Number, task.Title)

	prompt := fmt.Sprintf("You are an expert AI developer. Based on the following GitHub issue, create a concise, step-by-step plan to resolve it. Issue Title: %s. Issue Body: %s.", task.Title, task.Description)
	plan, err := reasoning.GenerateResponse(i.ctx, "phi3", prompt)
	if err != nil {
		fmt.Printf("❌ Failed to generate execution plan for task #%d: %v\n", task.Number, err)
		return
	}
	fmt.Printf("📝 Generated Plan for task #%d:\n%s\n", task.Number, plan)

	i.discussionLock.Lock()
	i.activeDiscussions[task.Number] = &Conversation{
		TaskID:          task.Number,
		TaskTitle:       task.Title,
		TaskDescription: task.Description,
		History:         []string{fmt.Sprintf("Plan by %s: %s", i.config.AgentID, plan)},
		LastUpdated:     time.Now(),
	}
	i.discussionLock.Unlock()

	metaMsg := map[string]interface{}{
		"issue_id":  task.Number,
		"message":   "Here is my proposed plan of action. What are your thoughts?",
		"plan":      plan,
	}
	if err := i.pubsub.PublishAntennaeMessage(pubsub.MetaDiscussion, metaMsg); err != nil {
		fmt.Printf("⚠️ Failed to publish plan to meta-discussion channel: %v\n", err)
	}
}

// handleMetaDiscussion is the core handler for incoming Antennae messages.
func (i *Integration) handleMetaDiscussion(msg pubsub.Message, from peer.ID) {
	issueID, ok := msg.Data["issue_id"].(float64)
	if !ok {
		return
	}
	taskID := int(issueID)

	i.discussionLock.Lock()
	convo, exists := i.activeDiscussions[taskID]
	if !exists || convo.IsEscalated {
		i.discussionLock.Unlock()
		return
	}

	incomingMessage, _ := msg.Data["message"].(string)
	convo.History = append(convo.History, fmt.Sprintf("Response from %s: %s", from.ShortString(), incomingMessage))
	convo.LastUpdated = time.Now()
	i.discussionLock.Unlock()

	fmt.Printf("🎯 Received peer feedback for task #%d. Reasoning about a response...\n", taskID)

	historyStr := strings.Join(convo.History, "\n")
	prompt := fmt.Sprintf(
		"You are an AI developer collaborating on a task. "+
			"This is the original task: Title: %s, Body: %s. "+
			"This is the conversation so far:\n%s\n\n"+
			"Based on the last message, provide a concise and helpful response.",
		convo.TaskTitle, convo.TaskDescription, historyStr,
	)

	response, err := reasoning.GenerateResponse(i.ctx, "phi3", prompt)
	if err != nil {
		fmt.Printf("❌ Failed to generate response for task #%d: %v\n", taskID, err)
		return
	}

	// Check if the situation requires human intervention
	if i.shouldEscalate(response, convo.History) {
		fmt.Printf("🚨 Escalating task #%d for human review.\n", taskID)
		convo.IsEscalated = true
		go i.triggerHumanEscalation(convo, response)
		return
	}

	fmt.Printf("💬 Sending response for task #%d...\n", taskID)
	responseMsg := map[string]interface{}{
		"issue_id": taskID,
		"message":  response,
	}
	if err := i.pubsub.PublishAntennaeMessage(pubsub.MetaDiscussion, responseMsg); err != nil {
		fmt.Printf("⚠️ Failed to publish response for task #%d: %v\n", taskID, err)
	}
}

// shouldEscalate determines if a task needs human intervention.
func (i *Integration) shouldEscalate(response string, history []string) bool {
	// Rule 1: Check for keywords in the latest response
	lowerResponse := strings.ToLower(response)
	for _, keyword := range escalationKeywords {
		if strings.Contains(lowerResponse, keyword) {
			return true
		}
	}

	// Rule 2: Check if the conversation is too long
	if len(history) >= conversationHistoryLimit {
		return true
	}

	return false
}

// triggerHumanEscalation sends the conversation details to the N8N webhook.
func (i *Integration) triggerHumanEscalation(convo *Conversation, reason string) {
	// 1. Announce the escalation to other agents
	escalationMsg := map[string]interface{}{
		"issue_id": convo.TaskID,
		"message":  "This task has been escalated for human review. No further automated action will be taken.",
		"reason":   reason,
	}
	if err := i.pubsub.PublishAntennaeMessage(pubsub.MetaDiscussion, escalationMsg); err != nil {
		fmt.Printf("⚠️ Failed to publish escalation message for task #%d: %v\n", convo.TaskID, err)
	}

	// 2. Send the payload to the N8N webhook
	payload := map[string]interface{}{
		"task_id":          convo.TaskID,
		"task_title":       convo.TaskTitle,
		"escalation_agent": i.config.AgentID,
		"reason":           reason,
		"history":          strings.Join(convo.History, "\n"),
	}
	payloadBytes, err := json.Marshal(payload)
	if err != nil {
		fmt.Printf("❌ Failed to marshal escalation payload for task #%d: %v\n", convo.TaskID, err)
		return
	}

	req, err := http.NewRequestWithContext(i.ctx, "POST", humanEscalationWebhookURL, bytes.NewBuffer(payloadBytes))
	if err != nil {
		fmt.Printf("❌ Failed to create escalation request for task #%d: %v\n", convo.TaskID, err)
		return
	}
	req.Header.Set("Content-Type", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		fmt.Printf("❌ Failed to send escalation webhook for task #%d: %v\n", convo.TaskID, err)
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 300 {
		fmt.Printf("⚠️ Human escalation webhook for task #%d returned non-2xx status: %d\n", convo.TaskID, resp.StatusCode)
	} else {
		fmt.Printf("✅ Successfully escalated task #%d to human administrator.\n", convo.TaskID)
	}
}

// filterSuitableTasks filters tasks based on agent capabilities and task labels
func (i *Integration) filterSuitableTasks(tasks []*Task) []*Task {
	var suitable []*Task
	
	for _, task := range tasks {
		// Check if we can handle this task based on its labels or title keywords
		if i.canHandleTask(task) {
			suitable = append(suitable, task)
		}
	}
	
	fmt.Printf("🔍 Filtered %d suitable tasks from %d total tasks\n", len(suitable), len(tasks))
	return suitable
}

// canHandleTaskType checks if this agent can handle the given task type
func (i *Integration) canHandleTaskType(taskType string) bool {
	for _, capability := range i.config.Capabilities {
		if capability == taskType || capability == "general" || capability == "task-coordination" {
			return true
		}
	}
	return false
}

// canHandleTask determines if this agent can handle a specific task
func (i *Integration) canHandleTask(task *Task) bool {
	// Check task labels for capability matches
	for _, label := range task.Labels {
		if i.canHandleTaskType(label) {
			return true
		}
	}
	
	// Check title/description for keyword matches based on capabilities
	taskText := strings.ToLower(task.Title + " " + task.Description)
	
	for _, capability := range i.config.Capabilities {
		switch capability {
		case "code-generation", "coding":
			if strings.Contains(taskText, "code") || strings.Contains(taskText, "implement") || 
			   strings.Contains(taskText, "develop") || strings.Contains(taskText, "write") {
				return true
			}
		case "code-analysis", "review":
			if strings.Contains(taskText, "review") || strings.Contains(taskText, "analyze") || 
			   strings.Contains(taskText, "audit") || strings.Contains(taskText, "refactor") {
				return true
			}
		case "debugging", "bug-fix":
			if strings.Contains(taskText, "bug") || strings.Contains(taskText, "fix") || 
			   strings.Contains(taskText, "error") || strings.Contains(taskText, "debug") {
				return true
			}
		case "testing":
			if strings.Contains(taskText, "test") || strings.Contains(taskText, "spec") ||
			   strings.Contains(taskText, "validation") {
				return true
			}
		case "documentation":
			if strings.Contains(taskText, "doc") || strings.Contains(taskText, "readme") ||
			   strings.Contains(taskText, "guide") || strings.Contains(taskText, "manual") {
				return true
			}
		case "general", "task-coordination", "meta-discussion":
			// These capabilities can handle any task
			return true
		}
	}
	
	// If no specific match, check if we have general capabilities
	return i.canHandleTaskType("general")
}

// announceTaskClaim broadcasts task claim to P2P mesh for coordination
func (i *Integration) announceTaskClaim(task *Task) error {
	claimData := map[string]interface{}{
		"task_id":    task.ID,
		"task_number": task.Number,
		"task_title": task.Title,
		"agent_id":   i.config.AgentID,
		"timestamp":  time.Now().Unix(),
		"repository": fmt.Sprintf("%s/%s", task.Labels[0], task.Labels[1]), // Assuming owner/repo in labels
		"action":     "claimed",
	}
	
	fmt.Printf("📢 Announcing task claim to P2P mesh: Task #%d\n", task.Number)
	return i.pubsub.PublishBzzzMessage(pubsub.TaskClaim, claimData)
}