package remediationticket

import (
	"context"

	"github.com/go-logr/logr"
	userv1 "github.com/tidbcloud/aisupport/packages/sdk-go/contracts/api/core-server/user/v1"
	"github.com/tidbcloud/devops/entity"
	"github.com/tidbcloud/devops/pkg/infra/clients/larkcli"
	"github.com/tidbcloud/devops/pkg/infra/config"
	"github.com/tidbcloud/devops/pkg/infra/log"
	"github.com/tidbcloud/devops/pkg/infra/utils/container"
	"github.com/tidbcloud/devops/pkg/service"
	apprsvcapi "github.com/tidbcloud/devops/pkg/service/approval/api"
	"github.com/tidbcloud/devops/pkg/service/dashboard"
	incidentsvc "github.com/tidbcloud/devops/pkg/service/incident"
	"github.com/tidbcloud/devops/pkg/service/remediationticket/actors"
	rtsvcapi "github.com/tidbcloud/devops/pkg/service/remediationticket/api"
	rtphase "github.com/tidbcloud/devops/pkg/service/remediationticket/phase"
	"github.com/tidbcloud/devops/pkg/service/remediationticket/repo"
	"github.com/tidbcloud/devops/pkg/service/tenant"
	"github.com/tidbcloud/pkgv2/db"
	"go.uber.org/dig"
)

type ServiceParam struct {
	dig.In

	Conf             *config.RootConfig
	DBProvider       db.TransProvider
	Logger           logr.Logger
	UserService      userv1.UserServiceClient
	TenantService    tenant.TenantServiceAPI
	DashboardService dashboard.DashboardServiceAPI
	LarkCli          *larkcli.LarkClient
	IncidentService  incidentsvc.IncidentService
	Approval         apprsvcapi.ApprovalServiceAPI
}

type ServiceResult struct {
	dig.Out

	API     rtsvcapi.RemediationTicketServiceAPI
	Service service.Service `group:"service"`
}

type svc struct {
	*service.JobService

	logger       logr.Logger
	conf         config.RemediationTicketConfig
	dbp          db.TransProvider
	userSvc      userv1.UserServiceClient
	tenantSvc    tenant.TenantServiceAPI
	dashboardSvc dashboard.DashboardServiceAPI
	larkCliSet   *larkcli.LarkClient
	incidentSvc  incidentsvc.IncidentService
	apprSvc      apprsvcapi.ApprovalServiceAPI
	repo         repo.RemediationTicketRepo
	syncInsCh    chan uint64
}

func NewService(p ServiceParam) (ServiceResult, error) {
	base := service.NewJobService("RemediationTicketService", service.WithDummy(p.Conf.Services.RemediationTicket.Dummy))
	if p.Conf.Services.RemediationTicket.Dummy {
		return ServiceResult{
			API:     &rtsvcapi.DummyRemediationTicketServiceAPI{},
			Service: base,
		}, nil
	}

	s := &svc{
		JobService:   base,
		conf:         p.Conf.Services.RemediationTicket,
		logger:       p.Logger.WithName("RemediationTicketService"),
		dbp:          p.DBProvider,
		userSvc:      p.UserService,
		tenantSvc:    p.TenantService,
		dashboardSvc: p.DashboardService,
		larkCliSet:   p.LarkCli,
		incidentSvc:  p.IncidentService,
		apprSvc:      p.Approval,
		repo:         repo.NewRemediationTicketRepo(p.DBProvider),
		syncInsCh:    make(chan uint64, 100),
	}

	if err := rtphase.ConfigureWorkflow(s.conf.Workflow); err != nil {
		return ServiceResult{}, err
	}

	s.AddJob(
		service.NewConstantCronJob("WatchRemediationTickets", *s.conf.WatchRemediationTicketsInterval, s.WatchRemediationTickets),
		service.NewEventJob("SyncRemediationTickets", s.syncInsCh, s.SyncTickets),
	)

	return ServiceResult{API: s, Service: s}, nil
}

// s
func (s *svc) WatchRemediationTickets(ctx context.Context) error {
	logger := log.FromContext(ctx)

	statusToSync := []entity.RemediationTicketStatus{
		entity.RemediationTicketStatusApproving,
	}
	tickets, err := s.repo.QueryTicket(ctx, repo.RemediationTicketOpts.StatusIn(statusToSync))
	if err != nil {
		return err
	}

	if len(tickets) != 0 {
		logger.Info("syncing remediation tickets ...",
			"remediation_ticket_ids", container.TransformSlice(tickets, func(i *entity.RemediationTicket) uint64 { return i.ID }),
		)
	}

	for _, ticket := range tickets {
		s.syncInsCh <- ticket.ID
	}

	return nil
}

func (s *svc) WatchRemediationTicketsSlowly(ctx context.Context) error {
	logger := log.FromContext(ctx)

	statusToSync := []entity.RemediationTicketStatus{
		entity.RemediationTicketStatusApproving,
	}
	tickets, err := s.repo.QueryTicket(ctx, repo.RemediationTicketOpts.StatusIn(statusToSync))
	if err != nil {
		return err
	}

	if len(tickets) != 0 {
		logger.Info("syncing authorizations slowly ...",
			"auth_ids", container.TransformSlice(tickets, func(i *entity.RemediationTicket) uint64 { return i.ID }),
		)
	}

	for _, ticket := range tickets {
		s.syncInsCh <- ticket.ID
	}

	return nil
}

func (s *svc) SyncTickets(ctx context.Context, id uint64) error {
	ticket, err := s.repo.LoadTicket(ctx, id)
	if err != nil {
		return err
	}

	ctx = log.WrapContext(ctx, "ticket_id", ticket.ID, "ticket_phase", ticket.Phase, "ticket_status", ticket.Status)
	actor := actors.NewRemediationTicketActor(ctx, s.insComps(ctx), ticket)

	return s.dbp.Transaction(ctx, func(ctx context.Context) error {
		var err error

		switch ticket.Status {
		case entity.RemediationTicketStatusApproving:
			err = actor.ContinueIfApproved()
		}

		return err
	})
}

func (s *svc) insComps(ctx context.Context) actors.Components {
	return actors.Components{
		Logger:                log.FromContextOrDef(ctx, s.logger),
		RTConfig:              s.conf,
		ApprovalService:       s.apprSvc,
		RemediationTicketRepo: s.repo,
	}
}
