package repo

import (
	"context"
	"fmt"
	"time"

	"github.com/tidbcloud/devops/entity"
	ubent "github.com/tidbcloud/devops/entity/userbehavior"
	"github.com/tidbcloud/devops/pkg/infra/utils/repoopt"
	"github.com/tidbcloud/devops/pkg/service/userbehavior/repo/cvt"
	"github.com/tidbcloud/pkgv2/db"
)

type UserBehaviorRepo interface {
	LoadChat(ctx context.Context, id uint64) (*entity.LarkChat, error)
	LoadChatSimplified(ctx context.Context, id uint64) (*entity.LarkChatSimplified, error)
	QueryChats(ctx context.Context, opts ...repoopt.Option) ([]*entity.LarkChat, error)
	QueryChatSimplifieds(ctx context.Context, opts ...repoopt.Option) ([]*entity.LarkChatSimplified, error)
	SaveChat(ctx context.Context, chat *entity.LarkChat) error
	BatchSaveChat(ctx context.Context, chat []*entity.LarkChat) error
	UpsertChatByChatId(ctx context.Context, chat *entity.LarkChat) (*entity.LarkChat, error)

	LoadSubscription(ctx context.Context, id uint64) (*ubent.Subscription, error)
	QuerySubscriptions(ctx context.Context, opts ...repoopt.Option) ([]*ubent.Subscription, error)
	SaveSubscription(ctx context.Context, subscription *ubent.Subscription) error
	PatchSubscription(ctx context.Context, subscription *ubent.Subscription) error
	DeleteSubscription(ctx context.Context, id uint64) error

	// GTM methods
	CreateGTMLead(ctx context.Context, lead *ubent.GTMLead) error
	UpdateGTMLeadUseCaseMatch(ctx context.Context, id uint64, useCase string, reason string) error
	CreateGTMSentEmail(ctx context.Context, email *ubent.GTMSentEmail) error
	ListDueGTMSentEmails(ctx context.Context, now time.Time, limit int) ([]*ubent.GTMSentEmail, error)
	MarkGTMSentEmailSending(ctx context.Context, id uint64) (bool, error)
	MarkGTMSentEmailSent(ctx context.Context, id uint64, sentAt time.Time, messageID string) error
	MarkGTMSentEmailFailed(ctx context.Context, id uint64, attemptedAt time.Time, errMsg string) error
	SuppressGTMEmail(ctx context.Context, suppression *ubent.GTMEmailSuppression) (*SuppressGTMEmailResult, error)
	IsGTMEmailSuppressed(ctx context.Context, tenantID uint64, campaignName string) (bool, error)
}

type SuppressGTMEmailResult struct {
	CancelledCount       int64
	AlreadySentOrSending bool
	Suppression          *ubent.GTMEmailSuppression
}

func NewUBRepo(dbp db.Provider) UserBehaviorRepo {
	driver := &driver{
		cvter:      cvt.NewConverter(),
		dbProvider: dbp,
	}

	return &repo{
		subDriver:          (*subscriptionDriver)(driver),
		chatDriver:         (*larkChatDriver)(driver),
		gtmLeadDriver:      (*gtmLeadDriver)(driver),
		gtmSentEmailDriver: (*gtmSentEmailDriver)(driver),
		gtmSuppressDriver:  (*gtmEmailSuppressionDriver)(driver),
	}
}

type driver struct {
	cvter      *cvt.Converter
	dbProvider db.Provider
}

type repo struct {
	subDriver          SubscriptionDriver
	chatDriver         LarkChatDriver
	gtmLeadDriver      GTMLeadDriver
	gtmSentEmailDriver GTMSentEmailDriver
	gtmSuppressDriver  GTMEmailSuppressionDriver
}

func (r *repo) QuerySubscriptions(ctx context.Context, opts ...repoopt.Option) ([]*ubent.Subscription, error) {
	return r.subDriver.Query(ctx, opts...)
}

func (r *repo) LoadSubscription(ctx context.Context, id uint64) (*ubent.Subscription, error) {
	return r.subDriver.Load(ctx, id)
}

func (r *repo) CreateSubscription(ctx context.Context, sub *ubent.Subscription) (*ubent.Subscription, error) {
	if sub == nil {
		return nil, fmt.Errorf("creation shouln't be nil")
	}

	return r.subDriver.Save(ctx, sub)
}

func (r *repo) SaveSubscription(ctx context.Context, patch *ubent.Subscription) error {
	_, err := r.subDriver.Save(ctx, patch)
	return err
}

func (r *repo) PatchSubscription(ctx context.Context, patch *ubent.Subscription) error {
	_, err := r.subDriver.Patch(ctx, patch)
	return err
}

func (r *repo) CountSubscription(ctx context.Context, opts ...repoopt.Option) (int64, error) {
	return r.subDriver.Count(ctx, opts...)
}

func (r *repo) DeleteSubscription(ctx context.Context, id uint64) error {
	return nil

}
func (r *repo) LoadChat(ctx context.Context, id uint64) (*entity.LarkChat, error) {
	return r.chatDriver.Load(ctx, id)

}
func (r *repo) LoadChatSimplified(ctx context.Context, id uint64) (*entity.LarkChatSimplified, error) {
	return r.chatDriver.LoadSimplified(ctx, id)
}

func (r *repo) QueryChats(ctx context.Context, opts ...repoopt.Option) ([]*entity.LarkChat, error) {
	return r.chatDriver.Query(ctx, opts...)

}

func (r *repo) QueryChatSimplifieds(ctx context.Context, opts ...repoopt.Option) ([]*entity.LarkChatSimplified, error) {
	return r.chatDriver.QuerySimplified(ctx, opts...)
}

func (r *repo) SaveChat(ctx context.Context, chat *entity.LarkChat) error {
	if chat == nil {
		return fmt.Errorf("chat shouldn't be nil")
	}

	_, err := r.chatDriver.Save(ctx, chat)
	return err
}

func (r *repo) BatchSaveChat(ctx context.Context, chats []*entity.LarkChat) error {
	for _, chat := range chats {
		_, err := r.chatDriver.Save(ctx, chat)
		if err != nil {
			return err
		}
	}
	return nil
}

func (r *repo) UpsertChatByChatId(ctx context.Context, chat *entity.LarkChat) (*entity.LarkChat, error) {
	if chat == nil {
		return nil, fmt.Errorf("chat shouldn't be nil")
	}

	if chat.ChatID == nil {
		return nil, fmt.Errorf("ChatID shouldn't be nil")
	}

	return r.chatDriver.UpsertByLarkChatId(ctx, chat)
}

// GTM methods
func (r *repo) CreateGTMLead(ctx context.Context, lead *ubent.GTMLead) error {
	created, err := r.gtmLeadDriver.Create(ctx, lead)
	if err != nil {
		return err
	}
	*lead = *created
	return nil
}

func (r *repo) UpdateGTMLeadUseCaseMatch(ctx context.Context, id uint64, useCase string, reason string) error {
	return r.gtmLeadDriver.UpdateLeadUseCaseMatch(ctx, id, useCase, reason)
}

func (r *repo) CreateGTMSentEmail(ctx context.Context, email *ubent.GTMSentEmail) error {
	created, err := r.gtmSentEmailDriver.Create(ctx, email)
	if err != nil {
		return err
	}
	*email = *created
	return nil
}

func (r *repo) ListDueGTMSentEmails(ctx context.Context, now time.Time, limit int) ([]*ubent.GTMSentEmail, error) {
	return r.gtmSentEmailDriver.ListDue(ctx, now, limit)
}

func (r *repo) MarkGTMSentEmailSending(ctx context.Context, id uint64) (bool, error) {
	return r.gtmSentEmailDriver.MarkSending(ctx, id)
}

func (r *repo) MarkGTMSentEmailSent(ctx context.Context, id uint64, sentAt time.Time, messageID string) error {
	return r.gtmSentEmailDriver.MarkSent(ctx, id, sentAt, messageID)
}

func (r *repo) MarkGTMSentEmailFailed(ctx context.Context, id uint64, attemptedAt time.Time, errMsg string) error {
	return r.gtmSentEmailDriver.MarkFailed(ctx, id, attemptedAt, errMsg)
}

func (r *repo) SuppressGTMEmail(ctx context.Context, suppression *ubent.GTMEmailSuppression) (*SuppressGTMEmailResult, error) {
	persisted, err := r.gtmSuppressDriver.Upsert(ctx, suppression)
	if err != nil {
		return nil, err
	}

	cancelledCount, err := r.gtmSentEmailDriver.CancelQueuedByTenant(ctx, suppression.TenantID)
	if err != nil {
		return nil, err
	}

	alreadySentOrSending, err := r.gtmSentEmailDriver.HasSendingOrSentByTenant(ctx, suppression.TenantID)
	if err != nil {
		return nil, err
	}

	return &SuppressGTMEmailResult{
		CancelledCount:       cancelledCount,
		AlreadySentOrSending: alreadySentOrSending,
		Suppression:          persisted,
	}, nil
}

func (r *repo) IsGTMEmailSuppressed(ctx context.Context, tenantID uint64, campaignName string) (bool, error) {
	return r.gtmSuppressDriver.Exists(ctx, tenantID, campaignName)
}
