package service

import (
	"context"
	"fmt"

	lark "github.com/larksuite/oapi-sdk-go/v3"
	larkbitable "github.com/larksuite/oapi-sdk-go/v3/service/bitable/v1"
	"github.com/spf13/viper"
	"go.uber.org/zap"

	"github.com/pingcap-inc/data-ingestion-tools/internal/itcronjob/store"
	"github.com/pingcap-inc/data-ingestion-tools/pkg/log"
)

type LarkSrv interface {
	GetClient(ctx context.Context) *lark.Client
	GetBitRecords(ctx context.Context, cloud string) ([]*larkbitable.AppTableRecord, error)
	BatchDeleteRecords(ctx context.Context, cloud string, records []*larkbitable.AppTableRecord) error
	WriteToBi(ctx context.Context, cloud string, fields []map[string]interface{}) error
}

type larkService struct {
	store store.Factory
}

var _ LarkSrv = (*larkService)(nil)

func newLark(str *service) *larkService {
	return &larkService{store: str.store}
}

const (
	appToken   = "bascng0RFPwn1xZalypTZ5ByXFg"
	dmsTableId = "tblY3FAPXQRZHBTR"
	osTableId  = "tblHxaWqgtXunqW6"
	dmsViewId  = "vewzwOS28F"
	osViewId   = "vew2do942j"
)

func (l *larkService) GetClient(ctx context.Context) *lark.Client {
	id := viper.GetString("lark.id")
	secret := viper.GetString("lark.secret")
	client := lark.NewClient(id, secret)
	return client
}
func (l *larkService) GetBitRecords(ctx context.Context, cloud string) ([]*larkbitable.AppTableRecord, error) {
	client := l.GetClient(ctx)
	var biRecords []*larkbitable.AppTableRecord
	tableId, viewId := selectCloud(cloud)
	var pageToken string
	for {
		req := larkbitable.NewListAppTableRecordReqBuilder().
			AppToken(appToken).
			TableId(tableId).
			ViewId(viewId).
			Sort("").
			FieldNames("").
			TextFieldAsArray(true).
			UserIdType("user_id").
			DisplayFormulaRef(true).
			AutomaticFields(true).
			PageToken(pageToken).
			PageSize(500).
			Build()
		res, err := client.Bitable.AppTableRecord.List(ctx, req)
		if err != nil {
			log.L(ctx).Errorf("list bitable records failed", zap.Error(err))

			return nil, err
		}
		if !res.Success() {
			log.L(ctx).Errorf("list bitable records failed: %s", res.CodeError.Msg)

			return nil, fmt.Errorf("%s", res.CodeError.Msg)
		}
		biRecords = append(biRecords, res.Data.Items...)
		if !*res.Data.HasMore {
			break
		}
		pageToken = *res.Data.PageToken
	}

	return biRecords, nil
}

func (l *larkService) BatchDeleteRecords(ctx context.Context, cloud string, records []*larkbitable.AppTableRecord) error {
	client := l.GetClient(ctx)

	if len(records) == 0 {
		return nil
	}

	var recordsId []string
	for _, record := range records {
		recordsId = append(recordsId, *record.RecordId)
	}
	tableId, _ := selectCloud(cloud)
	req := larkbitable.NewBatchDeleteAppTableRecordReqBuilder().
		AppToken(appToken).
		TableId(tableId).
		Body(larkbitable.NewBatchDeleteAppTableRecordReqBodyBuilder().
			Records(recordsId).Build()).
		Build()

	res, err := client.Bitable.AppTableRecord.BatchDelete(ctx, req)
	if err != nil {
		log.L(ctx).Errorf("batch delete records failed", zap.Error(err))

		return err
	}
	if !res.Success() {
		log.L(ctx).Errorf("batch delete records failed: %s", res.CodeError.Msg)

		return fmt.Errorf("%s", res.CodeError.Msg)
	}

	return nil
}

func (l *larkService) WriteToBi(ctx context.Context, cloud string, fields []map[string]interface{}) error {

	client := l.GetClient(ctx)
	var records []*larkbitable.AppTableRecord
	for _, field := range fields {
		records = append(records, &larkbitable.AppTableRecord{Fields: field})
	}
	tableId, _ := selectCloud(cloud)
	body := larkbitable.NewBatchCreateAppTableRecordReqBodyBuilder().Records(records).Build()
	req := larkbitable.NewBatchCreateAppTableRecordReqBuilder().AppToken(appToken).TableId(tableId).Body(body).Build()
	res, err := client.Bitable.AppTableRecord.BatchCreate(ctx, req)
	if err != nil {
		log.L(ctx).Errorf("batch create records failed", zap.Error(err))

		return err
	}
	if !res.Success() {
		log.L(ctx).Errorf("batch create records failed: %s", res.CodeError.Msg)

		return fmt.Errorf("%s", res.CodeError.Msg)
	}
	return nil
}

func selectCloud(cloud string) (string, string) {
	switch cloud {
	case "dms":
		return dmsTableId, dmsViewId
	case "os":
		return osTableId, osViewId
	default:
		return "", ""
	}
}
