package chat

import (
	"context"
	"errors"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"hfb_sys/backend/internal/model"
	"time"
)

func (r *Repository) ListConversations(ctx context.Context, principal Principal, page, pageSize int) (*PaginatedResult, error) {
	page, pageSize = normalizePagination(page, pageSize)
	db := r.db.WithContext(ctx)
	var total int64
	countDB := db.Table("chat_conversations AS c").
		Joins("JOIN chat_participants AS cp ON cp.conversation_id = c.id").
		Where("cp.participant_type = ? AND cp.participant_id = ?", principal.Type, principal.ID)
	if err := countDB.Count(&total).Error; err != nil {
		return nil, err
	}

	var rows []conversationRow
	offset := (page - 1) * pageSize
	err := r.conversationQuery(ctx, principal).
		Order("COALESCE(c.last_message_at, c.created_at) DESC, c.id DESC").
		Offset(offset).
		Limit(pageSize).
		Scan(&rows).Error
	if err != nil {
		return nil, err
	}
	items := make([]ConversationDTO, 0, len(rows))
	for _, row := range rows {
		items = append(items, row.toDTO(nil))
	}
	return &PaginatedResult{Items: items, Total: total, Page: page, PageSize: pageSize}, nil
}
func (r *Repository) FindConversation(ctx context.Context, principal Principal, id uint64) (*ConversationDTO, error) {
	db := r.db.WithContext(ctx)
	// 管理员可以查看任意会话，无需是 participant
	if principal.Type == "admin" {
		var conversation model.ChatConversation
		if err := db.First(&conversation, id).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return nil, ErrConversationNotFound
			}
			return nil, err
		}
		participants, err := r.participants(ctx, conversation.ID)
		if err != nil {
			return nil, err
		}
		dto := ConversationDTO{
			ID:                 conversation.ID,
			OrderID:            conversation.OrderID,
			ListingID:          conversation.ListingID,
			Type:               conversation.Type,
			Title:              conversation.Title,
			Status:             conversation.Status,
			Role:               "admin", // 管理员角色
			Participants:       participants,
			LastMessageID:      conversation.LastMessageID,
			LastMessagePreview: conversation.LastMessagePreview,
			LastMessageAt:      conversation.LastMessageAt,
			UnreadCount:        0, // 管理员不计未读
			CreatedAt:          conversation.CreatedAt,
			UpdatedAt:          conversation.UpdatedAt,
		}
		return &dto, nil
	}

	// 普通用户需要是 participant
	var row conversationRow
	err := r.conversationQuery(ctx, principal).Where("c.id = ?", id).First(&row).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrConversationNotFound
		}
		return nil, err
	}
	participants, err := r.participants(ctx, row.ID)
	if err != nil {
		return nil, err
	}
	dto := row.toDTO(participants)
	return &dto, nil
}
func (r *Repository) FindOrderConversation(ctx context.Context, userID uint64, orderID uint64) (*ConversationDTO, error) {
	principal := Principal{Type: "user", ID: userID}
	var order model.RentalOrder
	if err := r.db.WithContext(ctx).First(&order, orderID).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrConversationNotFound
		}
		return nil, err
	}

	if order.ListingID > 0 {
		var row conversationRow
		err := r.conversationQuery(ctx, principal).
			Where("c.listing_id = ? AND c.type = ?", order.ListingID, ConversationTypeListingGroup).
			First(&row).Error
		if err == nil {
			participants, err := r.participants(ctx, row.ID)
			if err != nil {
				return nil, err
			}
			dto := row.toDTO(participants)
			return &dto, nil
		}
		if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, err
		}
	}

	var row conversationRow
	err := r.conversationQuery(ctx, principal).Where("c.order_id = ?", orderID).First(&row).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrConversationNotFound
		}
		return nil, err
	}
	participants, err := r.participants(ctx, row.ID)
	if err != nil {
		return nil, err
	}
	dto := row.toDTO(participants)
	return &dto, nil
}
func (r *Repository) EnsureSupportConversation(ctx context.Context, userID uint64) (*ConversationDTO, error) {
	var conversationID uint64
	err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var existing model.ChatConversation
		err := tx.Table("chat_conversations AS c").
			Select("c.*").
			Joins("JOIN chat_participants AS cp ON cp.conversation_id = c.id").
			Where("c.type = ? AND cp.participant_type = ? AND cp.participant_id = ?", "general_support", "user", userID).
			Order("c.id ASC").
			Limit(1).
			Find(&existing).Error
		if err != nil {
			return err
		}
		if existing.ID > 0 {
			conversationID = existing.ID
			return nil
		}

		now := time.Now()
		conversation := model.ChatConversation{
			Type:   "general_support",
			Title:  "平台客服",
			Status: "active",
		}
		if err := tx.Create(&conversation).Error; err != nil {
			return err
		}

		participants := []model.ChatParticipant{
			{
				ConversationID:  conversation.ID,
				ParticipantType: "user",
				ParticipantID:   userID,
				Role:            "customer",
				JoinedAt:        now,
			},
		}
		if supportID := defaultSupportAdminID(tx); supportID > 0 {
			participants = append(participants, model.ChatParticipant{
				ConversationID:  conversation.ID,
				ParticipantType: "admin",
				ParticipantID:   supportID,
				Role:            "support",
				JoinedAt:        now,
			})
		}
		for _, participant := range participants {
			if err := tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&participant).Error; err != nil {
				return err
			}
		}

		message := model.ChatMessage{
			ConversationID: conversation.ID,
			SenderType:     "system",
			SenderRole:     "system",
			ContentType:    "system",
			Content:        "您好，客服会尽快回复，请直接描述您遇到的问题。",
			AttachmentURLS: emptyJSONList(),
		}
		if err := tx.Create(&message).Error; err != nil {
			return err
		}
		conversation.LastMessageID = &message.ID
		conversation.LastMessagePreview = truncatePreview(message.Content)
		conversation.LastMessageAt = &message.CreatedAt
		if err := tx.Save(&conversation).Error; err != nil {
			return err
		}
		conversationID = conversation.ID
		return nil
	})
	if err != nil {
		return nil, err
	}
	item, err := r.FindConversation(ctx, Principal{Type: "user", ID: userID}, conversationID)
	if err != nil {
		return nil, err
	}
	r.NotifyNewConversation(conversationID)
	return item, nil
}
