package integration

import (
	"context"
	"crypto/sha256"
	"encoding/json"
	"fmt"
	"log"
	"time"

	"chorus.services/bzzz/pkg/dht"
	"chorus.services/bzzz/pkg/ucxl"
)

// DecisionPublisher handles publishing decisions to encrypted DHT storage
type DecisionPublisher struct {
	dhtStorage *dht.EncryptedDHTStorage
	enabled    bool
}

// Decision represents a decision made from a HMMM discussion
type Decision struct {
	Type            string                 `json:"type"`            // Event type (approval, warning, etc.)
	Content         string                 `json:"content"`         // Human-readable decision content
	Participants    []string               `json:"participants"`    // Who participated in the decision
	ConsensusLevel  float64                `json:"consensus_level"` // Strength of consensus (0.0-1.0)
	Timestamp       time.Time              `json:"timestamp"`       // When decision was made
	DiscussionID    string                 `json:"discussion_id"`   // Source discussion ID
	Confidence      float64                `json:"confidence"`      // AI confidence in decision extraction
	Metadata        map[string]interface{} `json:"metadata"`        // Additional decision metadata
	UCXLAddress     string                 `json:"ucxl_address"`    // Associated UCXL address
	ExpiresAt       *time.Time             `json:"expires_at,omitempty"` // Optional expiration
	Tags            []string               `json:"tags"`            // Decision tags
	RelatedDecisions []string              `json:"related_decisions,omitempty"` // Related decision hashes
}

// PublishResult contains the result of publishing a decision
type PublishResult struct {
	UCXLAddress string    `json:"ucxl_address"`
	DHTHash     string    `json:"dht_hash"`
	Success     bool      `json:"success"`
	PublishedAt time.Time `json:"published_at"`
	Error       string    `json:"error,omitempty"`
}

// NewDecisionPublisher creates a new decision publisher
func NewDecisionPublisher(dhtStorage *dht.EncryptedDHTStorage, enabled bool) *DecisionPublisher {
	return &DecisionPublisher{
		dhtStorage: dhtStorage,
		enabled:    enabled,
	}
}

// PublishDecision publishes a decision to the encrypted DHT storage
func (dp *DecisionPublisher) PublishDecision(ctx context.Context, ucxlAddr *ucxl.Address, decision *Decision) (*PublishResult, error) {
	result := &PublishResult{
		UCXLAddress: ucxlAddr.String(),
		PublishedAt: time.Now(),
	}
	
	if !dp.enabled {
		result.Error = "Decision publishing is disabled"
		log.Printf("📤 Decision publishing skipped (disabled): %s", ucxlAddr.String())
		return result, nil
	}
	
	// Enrich decision with UCXL address
	decision.UCXLAddress = ucxlAddr.String()
	
	// Serialize decision to JSON
	decisionJSON, err := json.Marshal(decision)
	if err != nil {
		result.Error = fmt.Sprintf("failed to serialize decision: %v", err)
		return result, fmt.Errorf("failed to serialize decision: %w", err)
	}
	
	// Determine creator role from UCXL address
	creatorRole := ucxlAddr.Role
	if creatorRole == "any" || creatorRole == "" {
		creatorRole = "contributor" // Default role for decisions
	}
	
	// Store in encrypted DHT
	err = dp.dhtStorage.StoreUCXLContent(
		ucxlAddr.String(),
		decisionJSON,
		creatorRole,
		"decision",
	)
	
	if err != nil {
		result.Error = err.Error()
		return result, fmt.Errorf("failed to store decision in DHT: %w", err)
	}
	
	// Generate content hash for reference
	result.DHTHash = fmt.Sprintf("sha256:%x", sha256.Sum256(decisionJSON))
	result.Success = true
	
	log.Printf("📤 Decision published to DHT: %s (hash: %s)", ucxlAddr.String(), result.DHTHash[:16]+"...")
	return result, nil
}

// RetrieveDecision retrieves a decision from the encrypted DHT storage
func (dp *DecisionPublisher) RetrieveDecision(ctx context.Context, ucxlAddr *ucxl.Address) (*Decision, error) {
	if !dp.enabled {
		return nil, fmt.Errorf("decision publishing is disabled")
	}
	
	// Retrieve from encrypted DHT
	content, metadata, err := dp.dhtStorage.RetrieveUCXLContent(ucxlAddr.String())
	if err != nil {
		return nil, fmt.Errorf("failed to retrieve decision from DHT: %w", err)
	}
	
	// Verify content type
	if metadata.ContentType != "decision" {
		return nil, fmt.Errorf("content at address is not a decision (type: %s)", metadata.ContentType)
	}
	
	// Deserialize decision
	var decision Decision
	if err := json.Unmarshal(content, &decision); err != nil {
		return nil, fmt.Errorf("failed to deserialize decision: %w", err)
	}
	
	log.Printf("📥 Decision retrieved from DHT: %s", ucxlAddr.String())
	return &decision, nil
}

// ListDecisionsByRole lists decisions accessible by a specific role
func (dp *DecisionPublisher) ListDecisionsByRole(ctx context.Context, role string, limit int) ([]*Decision, error) {
	if !dp.enabled {
		return nil, fmt.Errorf("decision publishing is disabled")
	}
	
	// Get content metadata from DHT
	metadataList, err := dp.dhtStorage.ListContentByRole(role, limit)
	if err != nil {
		return nil, fmt.Errorf("failed to list content by role: %w", err)
	}
	
	decisions := make([]*Decision, 0)
	
	// Retrieve each decision
	for _, metadata := range metadataList {
		if metadata.ContentType != "decision" {
			continue // Skip non-decisions
		}
		
		// Parse UCXL address
		addr, err := ucxl.Parse(metadata.Address)
		if err != nil {
			log.Printf("⚠️ Invalid UCXL address in decision metadata: %s", metadata.Address)
			continue
		}
		
		// Retrieve decision content
		decision, err := dp.RetrieveDecision(ctx, addr)
		if err != nil {
			log.Printf("⚠️ Failed to retrieve decision %s: %v", metadata.Address, err)
			continue
		}
		
		decisions = append(decisions, decision)
		
		// Respect limit
		if len(decisions) >= limit {
			break
		}
	}
	
	log.Printf("📋 Listed %d decisions for role: %s", len(decisions), role)
	return decisions, nil
}

// UpdateDecision updates an existing decision or creates a new version
func (dp *DecisionPublisher) UpdateDecision(ctx context.Context, ucxlAddr *ucxl.Address, decision *Decision) (*PublishResult, error) {
	if !dp.enabled {
		result := &PublishResult{
			UCXLAddress: ucxlAddr.String(),
			PublishedAt: time.Now(),
			Error:       "Decision publishing is disabled",
		}
		return result, nil
	}
	
	// Check if decision already exists
	existingDecision, err := dp.RetrieveDecision(ctx, ucxlAddr)
	if err == nil {
		// Decision exists, create related decision reference
		decision.RelatedDecisions = append(decision.RelatedDecisions, dp.generateDecisionHash(existingDecision))
		log.Printf("📝 Updating existing decision: %s", ucxlAddr.String())
	} else {
		log.Printf("📝 Creating new decision: %s", ucxlAddr.String())
	}
	
	// Publish the updated/new decision
	return dp.PublishDecision(ctx, ucxlAddr, decision)
}

// SearchDecisions searches for decisions matching criteria
func (dp *DecisionPublisher) SearchDecisions(ctx context.Context, searchCriteria map[string]string, limit int) ([]*Decision, error) {
	if !dp.enabled {
		return nil, fmt.Errorf("decision publishing is disabled")
	}
	
	// Convert search criteria to DHT search query
	query := &dht.SearchQuery{
		Agent:       searchCriteria["agent"],
		Role:        searchCriteria["role"],
		Project:     searchCriteria["project"],
		Task:        searchCriteria["task"],
		ContentType: "decision",
		Limit:       limit,
	}
	
	// Parse time filters if provided
	if createdAfter := searchCriteria["created_after"]; createdAfter != "" {
		if t, err := time.Parse(time.RFC3339, createdAfter); err == nil {
			query.CreatedAfter = t
		}
	}
	
	if createdBefore := searchCriteria["created_before"]; createdBefore != "" {
		if t, err := time.Parse(time.RFC3339, createdBefore); err == nil {
			query.CreatedBefore = t
		}
	}
	
	// Search DHT for matching decisions
	searchResults, err := dp.dhtStorage.SearchContent(query)
	if err != nil {
		return nil, fmt.Errorf("failed to search decisions: %w", err)
	}
	
	decisions := make([]*Decision, 0, len(searchResults))
	
	// Retrieve each decision
	for _, metadata := range searchResults {
		// Parse UCXL address
		addr, err := ucxl.Parse(metadata.Address)
		if err != nil {
			log.Printf("⚠️ Invalid UCXL address in search results: %s", metadata.Address)
			continue
		}
		
		// Retrieve decision content
		decision, err := dp.RetrieveDecision(ctx, addr)
		if err != nil {
			log.Printf("⚠️ Failed to retrieve decision %s: %v", metadata.Address, err)
			continue
		}
		
		decisions = append(decisions, decision)
	}
	
	log.Printf("🔍 Search found %d decisions", len(decisions))
	return decisions, nil
}

// GetDecisionMetrics returns metrics about decisions in the system
func (dp *DecisionPublisher) GetDecisionMetrics(ctx context.Context) (map[string]interface{}, error) {
	if !dp.enabled {
		return map[string]interface{}{
			"enabled": false,
			"message": "Decision publishing is disabled",
		}, nil
	}
	
	// Get DHT storage metrics
	dhtMetrics := dp.dhtStorage.GetMetrics()
	
	// Add decision-specific metrics
	metrics := map[string]interface{}{
		"enabled":      true,
		"dht_storage":  dhtMetrics,
		"last_updated": time.Now(),
	}
	
	return metrics, nil
}

// generateDecisionHash generates a hash for a decision to use in references
func (dp *DecisionPublisher) generateDecisionHash(decision *Decision) string {
	// Create hash from key decision fields
	hashData := fmt.Sprintf("%s_%s_%s_%d",
		decision.Type,
		decision.UCXLAddress,
		decision.DiscussionID,
		decision.Timestamp.Unix(),
	)
	
	hash := sha256.Sum256([]byte(hashData))
	return fmt.Sprintf("decision_%x", hash[:8])
}

// IsEnabled returns whether decision publishing is enabled
func (dp *DecisionPublisher) IsEnabled() bool {
	return dp.enabled
}

// Enable enables decision publishing
func (dp *DecisionPublisher) Enable() {
	dp.enabled = true
	log.Printf("📤 Decision publishing enabled")
}

// Disable disables decision publishing
func (dp *DecisionPublisher) Disable() {
	dp.enabled = false
	log.Printf("🚫 Decision publishing disabled")
}