package maintenancewindow

import (
	"context"
	"time"

	"github.com/tidbcloud/pkgv2/db"
	"go.opentelemetry.io/otel"
	"google.golang.org/protobuf/types/known/emptypb"

	tidbmgmtservicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/account"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/eventbus"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/repository"
	dedicateclusersvc "github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/service/dedicated/cluster"
	serverlessclustersvc "github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/service/serverless/cluster"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/lease"
	"github.com/tidbcloud/tidb-management-service/utilities/workflow"
)

var (
	tracer = otel.Tracer("domain.maintenancewindow")
)

type Service interface {
	ListMaintenanceWindows(
		ctx context.Context,
		req *tidbmgmtservicev1.ListMaintenanceWindowsRequest,
	) (*tidbmgmtservicev1.ListMaintenanceWindowsResponse, bizerr.BizError)
	GetMaintenanceWindow(
		ctx context.Context,
		req *tidbmgmtservicev1.GetMaintenanceWindowRequest,
	) (*tidbmgmtservicev1.MaintenanceWindow, bizerr.BizError)
	UpdateMaintenanceWindow(
		ctx context.Context,
		req *tidbmgmtservicev1.UpdateMaintenanceWindowRequest,
	) (*tidbmgmtservicev1.MaintenanceWindow, bizerr.BizError)
	SyncMaintenanceWindow(
		ctx context.Context,
		req *tidbmgmtservicev1.SyncMaintenanceWindowRequest,
	) (*emptypb.Empty, bizerr.BizError)

	ListMaintenanceTasks(
		ctx context.Context,
		req *tidbmgmtservicev1.ListMaintenanceTasksRequest,
	) (*tidbmgmtservicev1.ListMaintenanceTasksResponse, bizerr.BizError)
	QueryMaintenanceTasks(
		ctx context.Context,
		req *tidbmgmtservicev1.QueryMaintenanceTasksRequest,
	) (*tidbmgmtservicev1.QueryMaintenanceTasksResponse, bizerr.BizError)
	GetMaintenanceTask(
		ctx context.Context,
		req *tidbmgmtservicev1.GetMaintenanceTaskRequest,
	) (*tidbmgmtservicev1.MaintenanceTask, bizerr.BizError)
	CountMaintenanceTasks(
		ctx context.Context,
		req *tidbmgmtservicev1.CountMaintenanceTasksRequest,
	) (*tidbmgmtservicev1.CountMaintenanceTasksResponse, bizerr.BizError)
	CreateMaintenanceTask(
		ctx context.Context,
		req *tidbmgmtservicev1.CreateMaintenanceTaskRequest,
	) (*tidbmgmtservicev1.MaintenanceTask, bizerr.BizError)
	UpdateMaintenanceTask(
		ctx context.Context,
		req *tidbmgmtservicev1.UpdateMaintenanceTaskRequest,
	) (*tidbmgmtservicev1.MaintenanceTask, bizerr.BizError)

	ListMaintenanceTaskExecutions(
		ctx context.Context,
		req *tidbmgmtservicev1.ListMaintenanceTaskExecutionsRequest,
	) (*tidbmgmtservicev1.ListMaintenanceTaskExecutionsResponse, bizerr.BizError)
	QueryMaintenanceTaskExecutions(
		ctx context.Context,
		req *tidbmgmtservicev1.QueryMaintenanceTaskExecutionsRequest,
	) (*tidbmgmtservicev1.QueryMaintenanceTaskExecutionsResponse, bizerr.BizError)
	UpdateMaintenanceTaskExecution(
		ctx context.Context,
		req *tidbmgmtservicev1.UpdateMaintenanceTaskExecutionRequest,
	) (*tidbmgmtservicev1.MaintenanceTaskExecution, bizerr.BizError)

	StartSyncMaintenanceWindowWorker()
}

type service struct {
	clusterRepo      repository.ClusterRepository
	mwRepo           repository.MaintenanceWindowRepo
	mtRepo           repository.MaintenanceTaskRepo
	mteRepo          repository.MaintenanceTaskExecutionRepo
	customStatusRepo repository.CustomStatusRepository
	accountCli       account.Client
	transaction      db.TransProvider
	eventCommitter   eventbus.WALCommitter

	workflowSvc workflow.Workflow

	serverlessClusterSvc serverlessclustersvc.Service
	dedicatedClusterSvc  dedicateclusersvc.Service

	// UT only
	currentApplyTime *time.Time
	leaseLocker      lease.Locker
}

func NewService(
	clusterRepo repository.ClusterRepository,
	mwRepo repository.MaintenanceWindowRepo,
	mtRepo repository.MaintenanceTaskRepo,
	mteRepo repository.MaintenanceTaskExecutionRepo,
	customStatusRepo repository.CustomStatusRepository,
	accountCli account.Client,
	transaction db.TransProvider,
	eventCommitter eventbus.WALCommitter,
	workflowSvc workflow.Workflow,
	serverlessClusterSvc serverlessclustersvc.Service,
	dedicateClusterSvc dedicateclusersvc.Service,
	leaseLocker lease.Locker,
) Service {
	svc := &service{
		clusterRepo:          clusterRepo,
		mwRepo:               mwRepo,
		mtRepo:               mtRepo,
		mteRepo:              mteRepo,
		customStatusRepo:     customStatusRepo,
		accountCli:           accountCli,
		transaction:          transaction,
		eventCommitter:       eventCommitter,
		workflowSvc:          workflowSvc,
		serverlessClusterSvc: serverlessClusterSvc,
		dedicatedClusterSvc:  dedicateClusterSvc,
		leaseLocker:          leaseLocker,
	}

	return svc
}
