package chat

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"time"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"hfb_sys/backend/internal/integrations/push"
	"hfb_sys/backend/internal/model"
	"hfb_sys/backend/internal/modules/supportgroup"
)

// 会话类型常量
const (
	ConversationTypeOrderGroup     = "order_group"
	ConversationTypeListingGroup   = "listing_group"
	ConversationTypeGeneralSupport = "general_support"
)

var (
	ErrListingConversationExists = errors.New("发布群已存在")
)

// EnsureListingConversation 确保发布群存在(幂等)
func EnsureListingConversation(tx *gorm.DB, listing model.RentalListing, preferredSupportAdminID uint64) (*model.ChatConversation, error) {
	// 1. 幂等查重
	var existing model.ChatConversation
	err := tx.Where("listing_id = ?", listing.ID).First(&existing).Error
	if err == nil {
		return &existing, nil
	}
	if !errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, err
	}

	// 2. 建会话
	now := time.Now()
	conversation := model.ChatConversation{
		ListingID: &listing.ID,
		Type:      ConversationTypeListingGroup,
		Title:     listingConversationTitle(listing),
		Status:    "active",
	}
	if err := tx.Create(&conversation).Error; err != nil {
		return nil, err
	}

	// 3. 写参与者: 号主 + 客服
	participants := []model.ChatParticipant{
		{
			ConversationID:  conversation.ID,
			ParticipantType: "user",
			ParticipantID:   listing.OwnerID,
			Role:            "owner",
			JoinedAt:        now,
		},
	}

	// 获取收号组客服。分组未配置时回退到开放上传管理员或默认客服。
	supportID, err := supportgroup.PickSupportAdmin(tx, supportgroup.GroupCodeOwnerOnboarding)
	if err != nil {
		return nil, err
	}
	if supportID <= 0 {
		supportID = preferredSupportAdminID
		if supportID <= 0 {
			supportID = defaultSupportAdminID(tx)
		}
	}
	if 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 nil, err
		}
	}

	// 4. 取二维码(带行锁)
	var qrcode *model.ChatQrCode
	var qrcodeImageURL string
	qrcodeObj, err := fetchUnusedQrCode(tx)
	if err != nil {
		return nil, err
	}
	if qrcodeObj != nil {
		qrcode = qrcodeObj
		qrcodeImageURL = qrcode.ImageURL
		// 标记二维码为已使用
		if err := markQrCodeAsUsed(tx, qrcode.ID, conversation.ID); err != nil {
			return nil, err
		}
	}

	// 5. 发欢迎语
	welcomeMessage := getListingGroupWelcomeMessage(tx)
	if err := sendSystemMessage(tx, conversation.ID, welcomeMessage); err != nil {
		return nil, err
	}

	// 6. 发二维码图片(如果有)
	if qrcodeImageURL != "" {
		if _, err := sendQrCodeImage(tx, conversation.ID, qrcodeImageURL); err != nil {
			return nil, err
		}
	} else {
		// 无二维码,发提示
		noQrTip := "客服企业微信群二维码补充中，请稍后在群内关注"
		if err := sendSystemMessage(tx, conversation.ID, noQrTip); err != nil {
			return nil, err
		}
		if err := createQrCodeDeliveryTask(tx, conversation.ID); err != nil {
			return nil, err
		}
	}

	// 7. 库存预警检查
	// 站内信在事务内写入；外部推送在事务提交后发送，避免事务回滚时误发。
	// 这里通过 goroutine 延迟 1 秒发送，给事务提交留出时间。
	// 极端情况下事务回滚仍可能误发，但仅是通知层面的轻微不一致，可接受。
	if qrcode != nil {
		if alert, err := checkQrCodeStockAndAlert(tx, &conversation); err == nil && alert != nil {
			go func(a pushAlert) {
				time.Sleep(time.Second)
				for _, p := range a.providers {
					_ = p.Send(context.Background(), a.message)
				}
			}(*alert)
		}
	}

	return &conversation, nil
}

// AddRenterToListingConversation 拉租客与卖号组客服进发布群
func AddRenterToListingConversation(tx *gorm.DB, listingID uint64, renterID uint64, orderNo string) error {
	// 1. 查找发布群
	var conv model.ChatConversation
	err := tx.Where("listing_id = ?", listingID).First(&conv).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			// 历史数据无发布群,优雅跳过
			return nil
		}
		return err
	}

	// 2. 幂等插入租客
	now := time.Now()
	participant := model.ChatParticipant{
		ConversationID:  conv.ID,
		ParticipantType: "user",
		ParticipantID:   renterID,
		Role:            "renter",
		JoinedAt:        now,
		LastReadAt:      &now,
	}

	if err := tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&participant).Error; err != nil {
		return err
	}

	// 3. 支付后由卖号组客服接入跟进交接和售后。
	handoffSupportID, err := supportgroup.PickSupportAdmin(tx, supportgroup.GroupCodeRenterHandoff)
	if err != nil {
		return err
	}
	if handoffSupportID > 0 {
		support := model.ChatParticipant{
			ConversationID:  conv.ID,
			ParticipantType: "admin",
			ParticipantID:   handoffSupportID,
			Role:            "support",
			JoinedAt:        now,
		}
		if err := tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&support).Error; err != nil {
			return err
		}
	}

	// 4. 发系统消息
	message := "租客已加入群聊(订单 " + orderNo + ")"
	if handoffSupportID > 0 {
		message += "，卖号组客服已接入"
	}
	return sendSystemMessage(tx, conv.ID, message)
}

// RemoveRenterFromListingConversation 移出租客，返回是否真的删除了租客成员记录。
func RemoveRenterFromListingConversation(tx *gorm.DB, listingID uint64, renterID uint64) (bool, error) {
	// 1. 查找发布群
	var conv model.ChatConversation
	err := tx.Where("listing_id = ?", listingID).First(&conv).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			// 无发布群,跳过
			return false, nil
		}
		return false, err
	}

	// 2. 删除租客参与者记录
	result := tx.Where("conversation_id = ? AND participant_type = ? AND participant_id = ? AND role = ?",
		conv.ID, "user", renterID, "renter").
		Delete(&model.ChatParticipant{})

	if result.Error != nil {
		return false, result.Error
	}

	// 3. 发系统消息(如果确实删除了)
	if result.RowsAffected > 0 {
		message := "订单已结束，租客已退出群聊"
		if err := sendSystemMessage(tx, conv.ID, message); err != nil {
			return false, err
		}
		return true, nil
	}

	return false, nil
}

// 辅助函数

func listingConversationTitle(listing model.RentalListing) string {
	return "账号群 " + listing.ListingNo
}

func getListingGroupWelcomeMessage(tx *gorm.DB) string {
	var cfg model.SystemConfig
	if err := tx.Where("`key` = ?", listingGroupWelcomeConfigKey).First(&cfg).Error; err == nil && cfg.Value != "" {
		return cfg.Value
	}

	return defaultListingGroupWelcome
}

func sendSystemMessage(tx *gorm.DB, conversationID uint64, content string) error {
	message := model.ChatMessage{
		ConversationID: conversationID,
		SenderType:     "system",
		SenderRole:     "system",
		ContentType:    "system",
		Content:        content,
		AttachmentURLS: emptyJSONList(),
	}

	if err := tx.Create(&message).Error; err != nil {
		return err
	}

	// 更新会话的最后消息
	return updateConversationLastMessage(tx, conversationID, &message)
}

func sendQrCodeImage(tx *gorm.DB, conversationID uint64, imageURL string) (*model.ChatMessage, error) {
	content := "👇 请扫码加入企业微信群"

	message := model.ChatMessage{
		ConversationID: conversationID,
		SenderType:     "system",
		SenderRole:     "system",
		ContentType:    "image",
		Content:        content,
		AttachmentURLS: encodeStringList([]string{imageURL}),
	}

	if err := tx.Create(&message).Error; err != nil {
		return nil, err
	}

	// 更新会话的最后消息
	if err := updateConversationLastMessage(tx, conversationID, &message); err != nil {
		return nil, err
	}
	return &message, nil
}

func updateConversationLastMessage(tx *gorm.DB, conversationID uint64, message *model.ChatMessage) error {
	updates := map[string]interface{}{
		"last_message_id":      message.ID,
		"last_message_preview": truncatePreview(message.Content),
		"last_message_at":      message.CreatedAt,
	}

	return tx.Model(&model.ChatConversation{}).
		Where("id = ?", conversationID).
		Updates(updates).Error
}

func fetchUnusedQrCode(tx *gorm.DB) (*model.ChatQrCode, error) {
	var qrcode model.ChatQrCode
	now := time.Now()

	err := tx.Where("status = ?", QrCodeStatusUnused).
		Where("expires_at IS NULL OR expires_at > ?", now).
		Order("id ASC").
		Limit(1).
		Clauses(clause.Locking{Strength: "UPDATE"}).
		First(&qrcode).Error

	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil // 无可用二维码,返回 nil 而非错误
		}
		return nil, err
	}

	return &qrcode, nil
}

func markQrCodeAsUsed(tx *gorm.DB, qrcodeID uint64, conversationID uint64) error {
	now := time.Now()
	return tx.Model(&model.ChatQrCode{}).
		Where("id = ?", qrcodeID).
		Updates(map[string]interface{}{
			"status":          QrCodeStatusUsed,
			"conversation_id": conversationID,
			"used_at":         now,
		}).Error
}

func createQrCodeDeliveryTask(tx *gorm.DB, conversationID uint64) error {
	task := model.ChatQrCodeDeliveryTask{
		ConversationID: conversationID,
		Status:         "pending",
	}
	return tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&task).Error
}

// pushAlert 事务提交后需要发送的外部推送。
type pushAlert struct {
	providers []push.Provider
	message   push.Message
}

func checkQrCodeStockAndAlert(tx *gorm.DB, conversation *model.ChatConversation) (*pushAlert, error) {
	// 从 push_rules 获取规则
	threshold := int64(5)
	ruleEnabled := true
	messageTemplate := "企业微信群二维码库存不足（剩余 {{.Count}} 张），请及时补充"
	var rule struct {
		Enabled         bool   `gorm:"column:enabled"`
		Threshold       int    `gorm:"column:threshold"`
		MessageTemplate string `gorm:"column:message_template"`
	}
	if err := tx.Table("push_rules").Where("event = ?", "qrcode_low_stock").First(&rule).Error; err == nil {
		ruleEnabled = rule.Enabled
		threshold = int64(rule.Threshold)
		if rule.MessageTemplate != "" {
			messageTemplate = rule.MessageTemplate
		}
	}

	if !ruleEnabled {
		return nil, nil
	}

	// 统计未使用的二维码数量
	var count int64
	if err := tx.Model(&model.ChatQrCode{}).
		Where("status = ?", QrCodeStatusUnused).
		Where("expires_at IS NULL OR expires_at > ?", time.Now()).
		Count(&count).Error; err != nil {
		return nil, err
	}

	if count > threshold {
		return nil, nil
	}

	// 低于阈值：写入站内信（事务内）
	var csAdmins []model.AdminUser
	if err := tx.Table("admin_users").
		Joins("JOIN admin_user_roles ON admin_users.id = admin_user_roles.admin_user_id").
		Joins("JOIN roles ON admin_user_roles.role_id = roles.id").
		Where("roles.code = ? AND admin_users.status = ?", "cs", "active").
		Select("admin_users.id").
		Find(&csAdmins).Error; err != nil {
		return nil, err
	}

	alertContent := strings.ReplaceAll(messageTemplate, "{{.Count}}", fmt.Sprintf("%d", count))

	entries := make([]map[string]interface{}, 0, len(csAdmins))
	now := time.Now()
	for _, admin := range csAdmins {
		entries = append(entries, map[string]interface{}{
			"admin_user_id": admin.ID,
			"type":          "system",
			"title":         "二维码库存预警",
			"content":       alertContent,
			"is_read":       false,
			"created_at":    now,
			"updated_at":    now,
		})
	}
	if len(entries) > 0 {
		if err := tx.Table("admin_notifications").Create(entries).Error; err != nil {
			return nil, err
		}
	}

	// 返回外部推送数据，由调用方在事务提交后发送
	providers := loadPushProviders(tx)
	if len(providers) == 0 {
		return nil, nil
	}
	return &pushAlert{
		providers: providers,
		message: push.Message{
			Title:   "二维码库存预警",
			Content: alertContent,
		},
	}, nil
}

// loadPushProviders 从 push_channels 表读取启用的渠道并创建 providers。
func loadPushProviders(tx *gorm.DB) []push.Provider {
	var providers []push.Provider
	var channels []struct {
		Type   string          `gorm:"column:type"`
		Config json.RawMessage `gorm:"column:config"`
	}
	if err := tx.Table("push_channels").Where("enabled = ?", true).Find(&channels).Error; err != nil {
		return providers
	}

	for _, ch := range channels {
		switch ch.Type {
		case "bark":
			var cfg struct {
				DeviceKey string `json:"device_key"`
				Server    string `json:"server"`
			}
			if err := json.Unmarshal(ch.Config, &cfg); err != nil || cfg.DeviceKey == "" {
				continue
			}
			bark, err := push.NewBarkProvider(push.BarkConfig{
				DeviceKey: cfg.DeviceKey,
				Server:    cfg.Server,
			})
			if err == nil {
				providers = append(providers, bark)
			}
		case "wpush":
			var cfg struct {
				APIKey string `json:"api_key"`
			}
			if err := json.Unmarshal(ch.Config, &cfg); err != nil || cfg.APIKey == "" {
				continue
			}
			wpush, err := push.NewWPushProvider(push.WPushConfig{APIKey: cfg.APIKey})
			if err == nil {
				providers = append(providers, wpush)
			}
		}
	}

	return providers
}
