package service

import (
	"errors"
	"fmt"
	"github.com/linmadan/egglib-go/core/application"
	pgTransaction "github.com/linmadan/egglib-go/transaction/pg"
	"github.com/zeromicro/go-zero/core/collection"
	"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/application/event/command"
	"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/application/factory"
	tablecommand "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/application/table/command"
	tableservice "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/application/table/service"
	"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
	"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/api/digitalLib"
	"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/cache"
	"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/domainService"
	"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/log"
	"time"
)

type TableEventService struct {
	TimingWheel *collection.TimingWheel
}

func (tableEventService *TableEventService) Handler(ctx *domain.Context, cmd *command.TableEventCommand) (interface{}, error) {
	transactionContext, err := factory.CreateTransactionContext(nil)
	if err != nil {
		return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
	}
	if err := transactionContext.StartTransaction(); err != nil {
		return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
	}
	defer func() {
		transactionContext.RollbackTransaction()
	}()

	data := cmd.EventTable
	tableId := 0
	switch data.Type {
	case domain.TableDataImportEvent, domain.TableDataEditEvent, domain.TableDeleteEvent, domain.TableStructEditEvent:
		tableId = data.Table.TableId
	case domain.QuerySetUpdateEvent:
		tableId = data.QuerySet.QuerySetInfo.BindTableId
	default:
		return nil, err
	}
	if tableId == 0 {
		return nil, nil
	}
	// tableId 相关联的
	tableRepository, _, _ := factory.FastPgTable(transactionContext, 0)
	_, tables, err := tableRepository.Find(map[string]interface{}{"context": data.Context, "tableTypesNotIn": []string{domain.TemporaryTable.ToString(), domain.ExcelTable.ToString()}})
	if err != nil {
		return nil, err
	}

	tableDependencyService, _ := domainService.NewTableDependencyService(transactionContext.(*pgTransaction.TransactionContext))
	tableDependTree := tableDependencyService.TableDependTree(tables, tableId)
	tree := tableDependTree.Tree

	tableService := tableservice.NewTableService(nil)
	for i := range tree {
		cache.DefaultDataTableCacheService.DeleteDataTable(tree[i])
		// fresh cache
		tableService.TablePreview(data.Context, &tablecommand.TablePreviewCommand{
			TableId:    tree[i],
			ObjectType: domain.ObjectMetaTable,
			PageSize:   10000,
			PageNumber: 0,
			UseCache:   true,
		})
	}

	if err := transactionContext.CommitTransaction(); err != nil {
		return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
	}
	return nil, nil
}

func (tableEventService *TableEventService) HandlerTableAffectedMarkToConflictStatus(ctx *domain.Context, cmd *command.TableEventCommand) (interface{}, error) {
	transactionContext, err := factory.CreateTransactionContext(nil)
	if err != nil {
		return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
	}
	if err := transactionContext.StartTransaction(); err != nil {
		return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
	}
	defer func() {
		transactionContext.RollbackTransaction()
	}()

	data := cmd.EventTable
	tableId := 0
	switch data.Type {
	case domain.TableStructEditEvent, domain.TableDeleteEvent:
		tableId = data.Table.TableId
	case domain.QuerySetUpdateEvent, domain.QuerySetUpdateRenameEvent:
		tableId = data.QuerySet.QuerySetInfo.BindTableId
	default:
		return nil, err
	}
	if tableId == 0 {
		return nil, nil
	}
	// tableId 相关联的
	tableRepository, _, _ := factory.FastPgTable(transactionContext, 0)

	_, tables, err := tableRepository.Find(map[string]interface{}{"context": ctx, "dependencyTable": tableId, "tableTypesNotIn": []string{domain.TemporaryTable.ToString()}})
	if errors.Is(err, domain.ErrorNotFound) {
		return nil, nil
	}
	tableIds := make([]int, 0)
	for _, table := range tables {
		tableIds = append(tableIds, table.TableId)
	}
	if len(tableIds) == 0 {
		return nil, nil
	}
	querySetRepository, _, _ := factory.FastPgQuerySet(transactionContext, 0)
	_, querySets, _ := querySetRepository.Find(map[string]interface{}{"context": ctx, "bindTableIds": tableIds})
	for _, querySet := range querySets {
		log.Logger.Debug(fmt.Sprintf("【集合状态更新】 id:%v name:%v ReadyStatus:1", querySet.QuerySetId, querySet.Name))
		querySet.QuerySetInfo.WithConflictStatus()
		_, err = querySetRepository.Save(querySet)
		if err != nil {
			return nil, err
		}
	}

	if err := transactionContext.CommitTransaction(); err != nil {
		return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
	}
	return nil, nil
}

func NewTableEventService(options map[string]interface{}) *TableEventService {
	svr := &TableEventService{}
	delayNotifyTimingWheel, _ := collection.NewTimingWheel(time.Second, 10, svr.TimingWheelFunc)
	svr.TimingWheel = delayNotifyTimingWheel
	return svr
}

func (tableEventService *TableEventService) TimingWheelFunc(key, value interface{}) {
	v, ok := value.(*NotifyData)
	if !ok {
		return
	}
	lib := digitalLib.NewDigitalLib("")
	if _, err := lib.SyncNotice(digitalLib.RequestSyncNotice{Body: v}); err != nil {
		log.Logger.Error(fmt.Sprintf("通知数控失败:%s", err.Error()))
		if t, ok := v.Retry(); ok {
			if err = tableEventService.TimingWheel.SetTimer(v.Key(), v, t); err == nil {
				log.Logger.Debug(fmt.Sprintf("通知数控重试(%d) key:%s wait:%vs", v.RetryTime(), v.Key(), t.Seconds()))
				return
			}
		}
	}
	tableEventService.TimingWheel.RemoveTimer(v.Key())
}