package message

import (
	"context"
	"fmt"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/core/domain"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
)

type OwnerQuoteSourceMessageQueryer interface {
	Query(context.Context, string, ...any) (coredb.Rows, error)
}

func LoadOwnerQuoteSourceMessages(ctx context.Context, queryer OwnerQuoteSourceMessageQueryer, workspaceID string, sourceMessageIDs []string) ([]OwnerQuoteSourceMessage, error) {
	requestedMessagesCTE, requestedMessageArgs, _, err := coredb.BuildIndexedStringValuesCTE("requested", "message_id", "ordinality", normalizeNonEmptyStrings(sourceMessageIDs), 2)
	if err != nil {
		return nil, err
	}

	query := fmt.Sprintf(`
WITH %s
SELECT
    m.id,
    m.scope_id,
    m.author_member_id,
    m.author_type,
    m.message_type,
    m.content,
    m.created_at,
    m.seq,
    m.workspace_global_seq
FROM requested
JOIN scope_messages m
  ON m.id = requested.message_id
WHERE m.workspace_id = $1
  AND `+MessageNotRewoundSQL("m")+`
ORDER BY requested.ordinality ASC
`, requestedMessagesCTE)
	args := append([]any{workspaceID}, requestedMessageArgs...)
	rows, err := queryer.Query(ctx, query, args...)
	if err != nil {
		return nil, fmt.Errorf("load owner_quote source messages: %w", err)
	}
	defer rows.Close()

	sources := make([]OwnerQuoteSourceMessage, 0, len(sourceMessageIDs))
	for rows.Next() {
		var source OwnerQuoteSourceMessage
		if err := rows.Scan(
			&source.ID,
			&source.SourceScopeID,
			&source.AuthorMemberID,
			&source.AuthorType,
			&source.MessageType,
			&source.Content,
			&source.CreatedAt,
			&source.Seq,
			&source.GlobalSeq,
		); err != nil {
			return nil, fmt.Errorf("scan owner_quote source message: %w", err)
		}
		sources = append(sources, source)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate owner_quote source messages: %w", err)
	}
	if len(sources) != len(sourceMessageIDs) {
		return nil, badRequest("source messages must belong to the current workspace")
	}
	return AttachOwnerQuoteAttachmentSnapshots(ctx, queryer, workspaceID, sources)
}

func ValidateOwnerQuoteSourceMessages(sources []OwnerQuoteSourceMessage) error {
	for _, source := range sources {
		if strings.TrimSpace(source.MessageType) != "" {
			return badRequest("owner quote source messages must be plain messages")
		}
		switch source.AuthorType {
		case string(domain.MemberTypeHuman), string(domain.MemberTypeAgent), string(domain.MemberTypeService):
		default:
			return badRequest("owner quote source messages must be authored by a participant")
		}
	}
	return nil
}

func AttachOwnerQuoteAttachmentSnapshots(ctx context.Context, queryer OwnerQuoteSourceMessageQueryer, workspaceID string, sources []OwnerQuoteSourceMessage) ([]OwnerQuoteSourceMessage, error) {
	if len(sources) == 0 {
		return sources, nil
	}

	messageIDs := make([]string, 0, len(sources))
	sourceIndexes := make(map[string]int, len(sources))
	for index, source := range sources {
		messageID := strings.TrimSpace(source.ID)
		if messageID == "" {
			continue
		}
		messageIDs = append(messageIDs, messageID)
		sourceIndexes[messageID] = index
	}
	if len(messageIDs) == 0 {
		return sources, nil
	}

	messageIDsCTE, messageIDArgs, _, err := coredb.BuildIndexedStringValuesCTE("message_ids", "message_id", "ordinality", messageIDs, 2)
	if err != nil {
		return nil, err
	}
	query := fmt.Sprintf(`
WITH %s
SELECT a.message_id, a.id, a.filename, a.content_type, a.size_bytes
FROM message_ids
JOIN attachments a USE INDEX (attachments_message_id_idx)
  ON a.message_id = message_ids.message_id
WHERE a.workspace_id = $1
ORDER BY message_ids.ordinality ASC, a.created_at ASC, a.id ASC
`, messageIDsCTE)
	args := append([]any{strings.TrimSpace(workspaceID)}, messageIDArgs...)
	rows, err := queryer.Query(ctx, query, args...)
	if err != nil {
		return nil, fmt.Errorf("load owner_quote attachment snapshots: %w", err)
	}
	defer rows.Close()

	for rows.Next() {
		var messageID string
		var attachment OwnerQuoteAttachmentSnapshot
		if err := rows.Scan(&messageID, &attachment.ID, &attachment.Filename, &attachment.ContentType, &attachment.SizeBytes); err != nil {
			return nil, fmt.Errorf("scan owner_quote attachment snapshot: %w", err)
		}
		index, ok := sourceIndexes[strings.TrimSpace(messageID)]
		if !ok {
			continue
		}
		sources[index].Attachments = append(sources[index].Attachments, attachment)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate owner_quote attachment snapshots: %w", err)
	}
	return sources, nil
}

func AppendOwnerQuoteAttachmentSnapshotLines(builder *strings.Builder, attachments []OwnerQuoteAttachmentSnapshot) {
	if len(attachments) == 0 {
		return
	}
	builder.WriteString("\nAttachments:\n")
	for _, attachment := range attachments {
		filename := strings.TrimSpace(attachment.Filename)
		if filename == "" {
			filename = strings.TrimSpace(attachment.ID)
		}
		builder.WriteString("- ")
		builder.WriteString(filename)
		details := make([]string, 0, 2)
		if contentType := strings.TrimSpace(attachment.ContentType); contentType != "" {
			details = append(details, contentType)
		}
		if attachment.SizeBytes > 0 {
			details = append(details, fmt.Sprintf("%d bytes", attachment.SizeBytes))
		}
		if len(details) > 0 {
			builder.WriteString(" (")
			builder.WriteString(strings.Join(details, ", "))
			builder.WriteString(")")
		}
		builder.WriteString("\n")
	}
}
