package message

import (
	"context"
	"fmt"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/model"
)

// ThreadShareSnapshotMessage is the minimal immutable message copy stored in a
// thread share card sidecar payload.
type ThreadShareSnapshotMessage struct {
	ID         string `json:"id"`
	AuthorName string `json:"authorName"`
	Content    string `json:"content"`
	CreatedAt  string `json:"createdAt"`
}

// ThreadShareCardView is the viewer-scoped state for an immutable reply-thread share card.
type ThreadShareCardView struct {
	MessageID                  string
	SourceParentChannelID      string
	SourceParentMessageID      string
	SourceParentMessageShortID string
	SourceLabel                string
	Title                      string
	Preview                    string
	MessageCount               int
	ParticipantCount           int
	CanOpenSource              bool
	Messages                   []ThreadShareSnapshotMessage
}

// ThreadShareCardTranscript is the persisted sidecar snapshot used to expand a
// shared-topic card into agent-readable history without changing browser/app
// message serialization.
type ThreadShareCardTranscript struct {
	SourceLabel      string                       `json:"sourceLabel"`
	Title            string                       `json:"title"`
	Preview          string                       `json:"preview"`
	MessageCount     int                          `json:"messageCount"`
	ParticipantCount int                          `json:"participantCount"`
	Messages         []ThreadShareSnapshotMessage `json:"messages"`
}

const (
	threadShareCardTranscriptMaxMessages          = 50
	threadShareCardTranscriptMessageContentRunes  = 500
	threadShareCardTranscriptExpansionBudgetRunes = 12000
)

func (transcript ThreadShareCardTranscript) empty() bool {
	return strings.TrimSpace(transcript.Title) == "" &&
		strings.TrimSpace(transcript.Preview) == "" &&
		transcript.MessageCount == 0 &&
		transcript.ParticipantCount == 0 &&
		len(transcript.Messages) == 0
}

func (service ScopeAPIService) loadThreadShareCardTranscripts(ctx context.Context, workspaceID string, refs []VisibleMessageRef) (map[string]ThreadShareCardTranscript, error) {
	workspaceID = strings.TrimSpace(workspaceID)
	if workspaceID == "" || len(refs) == 0 {
		return map[string]ThreadShareCardTranscript{}, nil
	}

	messageIDs := make([]string, 0)
	seen := make(map[string]struct{})
	for _, ref := range refs {
		if strings.TrimSpace(ref.Message.MessageType) != MessageTypeThreadShareCard {
			continue
		}
		messageID := strings.TrimSpace(ref.Message.ID)
		if messageID == "" {
			continue
		}
		if _, ok := seen[messageID]; ok {
			continue
		}
		seen[messageID] = struct{}{}
		messageIDs = append(messageIDs, messageID)
	}
	if len(messageIDs) == 0 {
		return map[string]ThreadShareCardTranscript{}, nil
	}

	requestedMessagesCTE, requestedMessageArgs, _, err := coredb.BuildStringValuesCTE("requested_messages", "message_id", messageIDs, 2)
	if err != nil {
		return nil, fmt.Errorf("build thread share card transcript query: %w", err)
	}
	rows, err := service.db.Query(ctx, fmt.Sprintf(`
WITH %s
SELECT sc.message_id, sc.payload
FROM requested_messages requested
JOIN scope_share_cards sc
  ON sc.workspace_id = $1
 AND sc.message_id = requested.message_id
`, requestedMessagesCTE), append([]any{workspaceID}, requestedMessageArgs...)...)
	if err != nil {
		return nil, fmt.Errorf("load thread share card transcripts: %w", err)
	}
	defer rows.Close()

	transcripts := make(map[string]ThreadShareCardTranscript, len(messageIDs))
	for rows.Next() {
		var messageID string
		var rawPayload []byte
		if err := rows.Scan(&messageID, &rawPayload); err != nil {
			return nil, fmt.Errorf("scan thread share card transcript: %w", err)
		}
		messageID = strings.TrimSpace(messageID)
		if messageID == "" || len(rawPayload) == 0 {
			continue
		}
		var transcript ThreadShareCardTranscript
		if err := model.UnmarshalJSONObject("thread share card transcript", rawPayload, &transcript); err != nil {
			continue
		}
		if transcript.empty() {
			continue
		}
		transcripts[messageID] = transcript
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate thread share card transcripts: %w", err)
	}
	return transcripts, nil
}

// FormatThreadShareCardTranscript returns the message content expanded with the
// immutable shared-topic sidecar snapshot for agent-readable history.
func FormatThreadShareCardTranscript(record ScopeMessageRecord, transcript ThreadShareCardTranscript) string {
	base := strings.TrimSpace(record.Content)
	if strings.TrimSpace(record.MessageType) != MessageTypeThreadShareCard || transcript.empty() {
		return record.Content
	}

	title := firstNonEmpty(transcript.Title, strings.TrimSpace(strings.TrimPrefix(base, "Shared topic:")), "shared topic")
	lines := make([]string, 0, 8+len(transcript.Messages))
	if base != "" {
		lines = append(lines, base)
	} else {
		lines = append(lines, "Shared topic: "+title)
	}
	lines = append(lines, "")
	lines = append(lines, "[Shared topic card snapshot]")
	lines = append(lines, "title: "+title)
	if source := strings.TrimSpace(transcript.SourceLabel); source != "" {
		lines = append(lines, "source: "+source)
	}
	if transcript.MessageCount > 0 {
		lines = append(lines, fmt.Sprintf("messages: %d", transcript.MessageCount))
	}
	if transcript.ParticipantCount > 0 {
		lines = append(lines, fmt.Sprintf("participants: %d", transcript.ParticipantCount))
	}
	if preview := compactThreadShareCardTranscriptText(transcript.Preview, threadShareCardTranscriptMessageContentRunes); preview != "" && preview != title {
		lines = append(lines, "preview: "+preview)
	}
	if len(transcript.Messages) > 0 {
		lines = append(lines, "snapshot messages:")
		visible := transcript.Messages
		if len(visible) > threadShareCardTranscriptMaxMessages {
			visible = visible[:threadShareCardTranscriptMaxMessages]
		}
		for i, snapshotMessage := range visible {
			author := firstNonEmpty(snapshotMessage.AuthorName, "unknown")
			content := compactThreadShareCardTranscriptText(snapshotMessage.Content, threadShareCardTranscriptMessageContentRunes)
			if content == "" {
				content = "(empty message)"
			}
			timestamp := strings.TrimSpace(snapshotMessage.CreatedAt)
			if timestamp != "" {
				lines = append(lines, fmt.Sprintf("%d. [%s] %s: %s", i+1, timestamp, author, content))
				continue
			}
			lines = append(lines, fmt.Sprintf("%d. %s: %s", i+1, author, content))
		}
		if omitted := len(transcript.Messages) - len(visible); omitted > 0 {
			lines = append(lines, fmt.Sprintf("... %d more snapshot message(s) omitted from agent history expansion.", omitted))
		}
	}

	return trimThreadShareCardTranscriptExpansion(strings.Join(lines, "\n"))
}

func compactThreadShareCardTranscriptText(content string, limit int) string {
	trimmed := strings.TrimSpace(strings.Join(strings.Fields(content), " "))
	if limit <= 0 || trimmed == "" {
		return trimmed
	}
	runes := []rune(trimmed)
	if len(runes) <= limit {
		return trimmed
	}
	return strings.TrimSpace(string(runes[:limit])) + "…"
}

func trimThreadShareCardTranscriptExpansion(content string) string {
	if threadShareCardTranscriptExpansionBudgetRunes <= 0 {
		return content
	}
	runes := []rune(content)
	if len(runes) <= threadShareCardTranscriptExpansionBudgetRunes {
		return content
	}
	return strings.TrimSpace(string(runes[:threadShareCardTranscriptExpansionBudgetRunes])) + "\n... shared topic snapshot truncated in agent history expansion."
}
