package repo

import (
	"context"
	"errors"
	"fmt"

	"github.com/tidbcloud/devops/entity"
	ubmod "github.com/tidbcloud/devops/models/userbehavior"
	"github.com/tidbcloud/devops/pkg/infra/utils/container"
	"github.com/tidbcloud/devops/pkg/infra/utils/repoopt"
	"gorm.io/gorm"
)

type LarkChatDriver interface {
	Save(ctx context.Context, subscription *entity.LarkChat) (*entity.LarkChat, error)
	Load(ctx context.Context, id uint64) (*entity.LarkChat, error)
	LoadSimplified(ctx context.Context, id uint64) (*entity.LarkChatSimplified, error)
	Query(ctx context.Context, opts ...repoopt.Option) ([]*entity.LarkChat, error)
	QuerySimplified(ctx context.Context, opts ...repoopt.Option) ([]*entity.LarkChatSimplified, error)
	Count(ctx context.Context, opts ...repoopt.Option) (int64, error)
	UpsertByLarkChatId(ctx context.Context, chat *entity.LarkChat) (*entity.LarkChat, error)
}

type larkChatDriver driver

func (d *larkChatDriver) Save(ctx context.Context, chatent *entity.LarkChat) (*entity.LarkChat, error) {
	wdb := d.dbProvider.WriteDB(ctx)
	chatmod := d.cvter.EntityToModel.LarkChat(chatent)

	err := wdb.Session(&gorm.Session{FullSaveAssociations: true}).Save(chatmod).Error
	if err != nil {
		return nil, fmt.Errorf("save subscription failed: %w", err)
	}

	// fill back id
	return d.cvter.ModelToEntity.LarkChat(chatmod), nil
}

func (d *larkChatDriver) Load(ctx context.Context, id uint64) (*entity.LarkChat, error) {
	rdb := d.dbProvider.ReadDB(ctx)
	chatMod := &ubmod.LarkChat{}

	err := d.buildModel(rdb).Where("id = ?", id).First(chatMod).Error
	if err != nil {
		return nil, fmt.Errorf("load subscription %d failed: %w", id, err)
	}

	return d.cvter.ModelToEntity.LarkChat(chatMod), nil
}

func (d *larkChatDriver) LoadSimplified(ctx context.Context, id uint64) (*entity.LarkChatSimplified, error) {
	rdb := d.dbProvider.ReadDB(ctx)
	chatmod := &ubmod.LarkChat{}

	err := d.buildModel(rdb).Where("id = ?", id).First(chatmod).Error
	if err != nil {
		return nil, fmt.Errorf("load subscription %d failed: %w", id, err)
	}

	return d.cvter.ModelToEntity.LarkChatSimplified(chatmod), nil
}

func (d *larkChatDriver) Query(ctx context.Context, opts ...repoopt.Option) ([]*entity.LarkChat, error) {
	rdb := d.dbProvider.ReadDB(ctx)
	chatmods := make([]*ubmod.LarkChat, 0)

	rdb = d.buildModel(rdb)
	rdb = repoopt.Aggregate(opts...).Apply(rdb)

	err := rdb.Find(&chatmods).Error
	if err != nil {
		return nil, fmt.Errorf("query subscriptions failed: %w", err)
	}

	return container.TransformSlice(chatmods, d.cvter.ModelToEntity.LarkChat), nil
}

func (d *larkChatDriver) QuerySimplified(ctx context.Context, opts ...repoopt.Option) ([]*entity.LarkChatSimplified, error) {
	rdb := d.dbProvider.ReadDB(ctx)
	chatmods := make([]*ubmod.LarkChat, 0)

	rdb = d.buildModel(rdb)
	rdb = repoopt.Aggregate(opts...).Apply(rdb)

	err := rdb.Find(&chatmods).Error
	if err != nil {
		return nil, fmt.Errorf("query subscriptions failed: %w", err)
	}

	return container.TransformSlice(chatmods, d.cvter.ModelToEntity.LarkChatSimplified), nil
}

func (d *larkChatDriver) Count(ctx context.Context, opts ...repoopt.Option) (int64, error) {
	rdb := d.dbProvider.ReadDB(ctx).Model(&ubmod.LarkChat{})
	rdb = repoopt.Aggregate(opts...).Apply(rdb)

	var count int64
	err := rdb.Count(&count).Error
	if err != nil {
		return 0, fmt.Errorf("count subscriptions failed: %w", err)
	}

	return count, nil
}

func (d *larkChatDriver) UpsertByLarkChatId(ctx context.Context, chatent *entity.LarkChat) (*entity.LarkChat, error) {
	wdb := d.dbProvider.WriteDB(ctx)
	chatmod := d.cvter.EntityToModel.LarkChat(chatent)
	dest := &ubmod.LarkChat{}

	result := wdb.Where("lark_chat_id = ?", chatmod.LarkChatID).First(dest)
	if errors.Is(result.Error, gorm.ErrRecordNotFound) {
		err := wdb.Save(chatmod).Error
		if err != nil {
			return nil, fmt.Errorf("create larkChat %v record failed: %w", chatent, err)
		}
		return chatent, nil
	}

	err := wdb.Model(chatmod).Where("lark_chat_id = ?", chatmod.LarkChatID).Select("lark_chat_name", "lark_chat_icon_url").Updates(chatmod).Error
	if err != nil {
		return nil, fmt.Errorf("update larkchat %v failed: %w", chatent, err)
	}

	return nil, nil
}

func (d *larkChatDriver) buildModel(db *gorm.DB) *gorm.DB {
	db = db.Model(&ubmod.LarkChat{})

	return db
}
