package handlers

import (
	"errors"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"strings"
	"time"
	"wa-assistant/backend/services"

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

// Handoff never expires into an automatic AI reply. An operator explicitly resumes.
const humanFacingHoldReply = services.DefaultHandoffReply

// Pending notices from earlier versions may still need delivery after an update.
const legacyHumanFacingHoldReply = "Untuk memastikan jawabannya tepat, saya teruskan percakapan ini ke rekan CS yang bisa membantu ya. Riwayatnya sudah disertakan, jadi tidak perlu mengulang dari awal."
const legacyCoordinatingHoldReply = "Sebentar ya, Kak. Saya koordinasikan dulu dengan tim terkait."

type handoffState struct {
	active bool
}

func resolveHandoffState(agentID uint, sender string) handoffState {
	var ho models.Handoff
	err := database.DB.Where("agent_id = ? AND sender = ?", agentID, sender).First(&ho).Error
	// Fail closed on storage errors; do not compete with a human operator.
	return handoffState{active: !errors.Is(err, gorm.ErrRecordNotFound)}
}

func handoffTranscript(rows []models.ChatHistory, lastMsg string) string {
	var b strings.Builder
	b.WriteString("Permintaan terakhir: " + truncateRunes(lastMsg, 700) + "\n\nPercakapan terkait:\n")
	for i := len(rows) - 1; i >= 0; i-- {
		row := services.DeliveredChatContext(rows[i])
		at := row.CreatedAt.Format("02 Jan 15:04")
		if row.Message != "" {
			b.WriteString(at + " Pelanggan: " + truncateRunes(row.Message, 350) + "\n")
		}
		if row.Reply != "" {
			b.WriteString(at + " Layanan: " + truncateRunes(row.Reply, 350) + "\n")
		}
	}
	return truncateRunes(b.String(), 5000)
}

func ensureHandoff(agentID uint, sender, lastMsg string) error {
	_, err := ensureHandoffDetails(agentID, sender, lastMsg, "needs_review")
	return err
}

func ensureHandoffDetails(agentID uint, sender, lastMsg, reason string) (bool, error) {
	_, created, err := saveHandoffDetails(agentID, sender, lastMsg, reason)
	return created, err
}

func saveHandoffDetails(agentID uint, sender, lastMsg, reason string) (models.Handoff, bool, error) {
	var saved models.Handoff
	created := false
	err := database.DB.Transaction(func(tx *gorm.DB) error {
		// Lock an existing parent row so repeated/parallel requests cannot create
		// duplicate handoffs, including installations with legacy duplicate rows.
		var agent models.Agent
		if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&agent, agentID).Error; err != nil {
			return err
		}
		var current models.Handoff
		err := tx.Where("agent_id = ? AND sender = ?", agentID, sender).First(&current).Error
		if err == nil {
			saved = current
			saved.LastMsg = lastMsg
			return tx.Model(&current).Update("last_msg", lastMsg).Error
		}
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return err
		}
		var rows []models.ChatHistory
		if err := tx.Select(services.ChatContextColumns).Where("agent_id = ? AND sender = ? AND revoked = ?", agentID, sender, false).Order("created_at DESC, id DESC").Limit(12).Find(&rows).Error; err != nil {
			return err
		}
		summary := handoffTranscript(rows, lastMsg)
		if tx.Migrator().HasTable(&models.AIFormSession{}) {
			var session models.AIFormSession
			if tx.Where("agent_id = ? AND sender = ? AND status IN ? AND expires_at > ?", agentID, sender, []string{"collecting", "confirming", "completed"}, time.Now()).First(&session).Error == nil {
				var form models.AIForm
				if tx.Where("id = ? AND agent_id = ?", session.FormID, agentID).First(&form).Error == nil {
					summary += "\n\nData layanan (" + session.Status + "):\n" + aiFormSummary(form, parseAIFormSteps(form), session.DataJSON)
				}
			}
		}
		row := models.Handoff{AgentID: agentID, Sender: sender, LastMsg: lastMsg, Status: "waiting", Reason: reason, Summary: summary}
		if err := tx.Create(&row).Error; err != nil {
			return err
		}
		created = true
		saved = row
		return enqueueHandoffNotification(tx, agent, row)
	})
	if err == nil {
		publishInboxEvent(agentID, sender, "handoff")
		if created {
			wakeHandoffNotifications()
		}
	}
	return saved, created && err == nil, err
}

// ClaimHandoff records ownership without sending any message to the customer.
func ClaimHandoff(c *gin.Context) {
	id, ok := resolveAgent(c)
	if !ok {
		return
	}
	sender := strings.TrimSpace(c.Param("sender"))
	var ho models.Handoff
	if err := database.DB.Where("agent_id = ? AND sender = ?", id, sender).First(&ho).Error; err != nil {
		c.JSON(404, gin.H{"error": "Antrean CS tidak ditemukan"})
		return
	}
	if !claimConversationForReply(c, id, sender) {
		return
	}
	now := time.Now()
	result := database.DB.Model(&models.Handoff{}).Where("id = ? AND (assigned_user_id = 0 OR assigned_user_id = ?)", ho.ID, currentUserID(c)).Updates(map[string]any{"status": "handling", "assigned_user_id": currentUserID(c), "claimed_at": now})
	if result.Error != nil {
		c.JSON(500, gin.H{"error": "Percakapan belum bisa diambil"})
		return
	}
	if result.RowsAffected == 0 {
		c.JSON(409, gin.H{"error": "Percakapan sedang ditangani CS lain"})
		return
	}
	publishInboxEvent(id, sender, "handoff")
	c.JSON(200, gin.H{"message": "Percakapan diambil. AI tetap dijeda."})
}

var errAutomaticReplyPaused = errors.New("automatic_reply_paused")

func handoffNoticeStillWaiting(agentID uint, sender string, handoffID uint) bool {
	if handoffID == 0 {
		return false
	}
	var count int64
	err := database.DB.Model(&models.Handoff{}).
		Where("id = ? AND agent_id = ? AND sender = ? AND status = ?", handoffID, agentID, sender, "waiting").Count(&count).Error
	return err == nil && count == 1
}

// Recheck after inference and immediately before a text send. The operator may
// have started replying while the provider was still generating an answer.
func automaticReplyStillAllowed(agentID uint, sender string) bool {
	if conversationHandledByCS(agentID, sender) {
		return false
	}
	var contact models.Contact
	err := database.DB.Select("manual_pause_until").Where("agent_id = ? AND number = ?", agentID, sender).First(&contact).Error
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return false
	}
	if contact.ManualPauseUntil != nil && contact.ManualPauseUntil.After(time.Now()) {
		return false
	}
	var count int64
	if err := database.DB.Model(&models.Handoff{}).Where("agent_id = ? AND sender = ? AND status = ?", agentID, sender, "handling").Count(&count).Error; err != nil {
		return false
	}
	return count == 0
}
