package handlers

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"strings"
	"time"

	"wa-assistant/backend/database"
	"wa-assistant/backend/models"
	"wa-assistant/backend/services"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

// GetConversationBrief — GET /agents/:id/conversation/brief?sender=
// Mengembalikan ringkasan operasional untuk CS (cache bila masih segar).
func GetConversationBrief(c *gin.Context) {
	id, ok := resolveAgent(c)
	if !ok {
		return
	}
	sender := strings.TrimSpace(c.Query("sender"))
	if sender == "" {
		c.JSON(400, gin.H{"error": "Parameter sender wajib"})
		return
	}
	force := c.Query("refresh") == "1" || c.Query("force") == "1"
	brief, err := loadOrBuildBrief(id, sender, force)
	if err != nil {
		c.JSON(502, gin.H{"error": "Ringkasan belum bisa dibuat: " + err.Error()})
		return
	}
	c.JSON(200, gin.H{"data": brief})
}

// RefreshConversationBrief — POST /agents/:id/conversation/brief  body: {"sender":"..."}
func RefreshConversationBrief(c *gin.Context) {
	id, ok := resolveAgent(c)
	if !ok {
		return
	}
	var req struct {
		Sender string `json:"sender"`
	}
	_ = c.ShouldBindJSON(&req)
	sender := strings.TrimSpace(req.Sender)
	if sender == "" {
		sender = strings.TrimSpace(c.Query("sender"))
	}
	if sender == "" {
		c.JSON(400, gin.H{"error": "Parameter sender wajib"})
		return
	}
	brief, err := loadOrBuildBrief(id, sender, true)
	if err != nil {
		c.JSON(502, gin.H{"error": "Ringkasan belum bisa dibuat: " + err.Error()})
		return
	}
	c.JSON(200, gin.H{"data": brief})
}

func loadOrBuildBrief(agentID uint, sender string, force bool) (services.ConversationBrief, error) {
	var mem models.ConversationMemory
	if err := database.DB.Where("agent_id = ? AND sender = ?", agentID, sender).First(&mem).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return services.ConversationBrief{}, err
	}
	var work models.ConversationWork
	if err := database.DB.Where("agent_id = ? AND sender = ?", agentID, sender).First(&work).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return services.ConversationBrief{}, err
	}
	var handoffs int64
	if err := database.DB.Model(&models.Handoff{}).Where("agent_id = ? AND sender = ?", agentID, sender).Count(&handoffs).Error; err != nil {
		return services.ConversationBrief{}, err
	}
	needsHuman := handoffs > 0 && work.Status != "resolved"

	// IDs reflect insertion order, which differs from WhatsApp chronology when
	// old history is imported. Read only the small, indexed recent message window.
	var recent []models.ChatHistory
	if err := database.DB.Select("id", "message", "reply", "from_human", "media_type", "file_name", "created_at", "delivery_status").
		Where("agent_id = ? AND sender = ? AND COALESCE(revoked, false) = false AND COALESCE(media_type, '') NOT IN ('call', 'system')", agentID, sender).
		Order("created_at desc").Order("id desc").Limit(80).Find(&recent).Error; err != nil {
		return services.ConversationBrief{}, err
	}
	// Include message contents: AI replies and edits may update the same row ID.
	contextJSON, err := json.Marshal(struct {
		Messages   []models.ChatHistory
		Memory     string
		NeedsHuman bool
		Work       models.ConversationWork
	}{recent, mem.Summary, needsHuman, work})
	if err != nil {
		return services.ConversationBrief{}, err
	}
	digest := sha256.Sum256(contextJSON)
	contextKey := hex.EncodeToString(digest[:])
	if !force {
		if cached, ok := services.DecodeBrief(mem.BriefJSON); ok && cached.Version == services.ConversationBriefVersion && cached.ContextKey == contextKey {
			cached.Stale = false
			return cached, nil
		}
	}
	var brief services.ConversationBrief
	if force && work.Status != "resolved" {
		brief, err = services.BuildConversationBrief(agentID, sender, recent, mem.Summary, needsHuman, true)
	} else {
		brief, err = services.BuildConversationBriefHeuristic(agentID, sender, recent, mem.Summary, needsHuman)
	}
	if err != nil {
		return brief, err
	}
	services.ApplyConversationWorkToBrief(&brief, work)
	brief.ContextKey = contextKey
	brief.Stale = false
	if err := storeConversationBrief(agentID, sender, brief); err != nil {
		return brief, err
	}
	return brief, nil
}

// storeConversationBrief mengubah kolom cache brief saja. Long-term memory dapat
// diperbarui goroutine AI pada waktu bersamaan; upsert selektif ini mencegah
// kedua penulis saling menimpa summary/checkpoint atau brief terbaru.
func storeConversationBrief(agentID uint, sender string, brief services.ConversationBrief) error {
	now := time.Now()
	row := models.ConversationMemory{
		AgentID:     agentID,
		Sender:      sender,
		BriefJSON:   services.EncodeBrief(brief),
		BriefChatID: brief.LastChatID,
		BriefAt:     &now,
	}
	return database.DB.Clauses(clause.OnConflict{
		Columns: []clause.Column{{Name: "agent_id"}, {Name: "sender"}},
		DoUpdates: clause.Assignments(map[string]any{
			"brief_json":    row.BriefJSON,
			"brief_chat_id": row.BriefChatID,
			"brief_at":      row.BriefAt,
			"updated_at":    now,
		}),
	}).Create(&row).Error
}
