//go:generate ${GOBIN}/mockgen -destination application_service_mock.go -package=application ${GO_MODULE}/pkg/service/application ApplicationService
package application

import (
	"context"
	"fmt"

	userv1 "github.com/tidbcloud/aisupport/packages/sdk-go/contracts/api/core-server/user/v1"
	appent "github.com/tidbcloud/devops/entity/application"
	"github.com/tidbcloud/devops/pkg/infra/config"
	"github.com/tidbcloud/devops/pkg/service"
	"github.com/tidbcloud/devops/pkg/service/application/repo"

	"github.com/go-logr/logr"
	"github.com/tidbcloud/pkgv2/db"
	"go.uber.org/dig"
)

var (
	_ ApplicationService = &svc{}
)

type ServiceParam struct {
	dig.In

	Conf       *config.RootConfig
	Logger     logr.Logger
	DBProvider db.TransProvider
	User       userv1.UserServiceClient
}

type ServiceResult struct {
	dig.Out

	API     ApplicationService
	Service service.Service `group:"service"`
}

type svc struct {
	*service.BaseService

	conf    config.ApplicationServiceConfig
	repo    repo.ApplicationRepo
	userSvc userv1.UserServiceClient
}

func NewService(p ServiceParam) (ServiceResult, error) {
	base := service.NewBaseService("ApplicationService")

	svc := &svc{
		BaseService: base,

		conf:    p.Conf.Services.Application,
		repo:    repo.NewApplicationRepo(p.DBProvider),
		userSvc: p.User,
	}

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

func (s *svc) ListApplications(ctx context.Context, param ListApplicationsParam) ([]*appent.Application, int64, error) {
	apps, err := s.repo.ListApplications(ctx)
	if err != nil {
		return apps, 0, err
	}

	return apps, 0, nil
}

func (s *svc) GetApplication(ctx context.Context, name string) (*appent.Application, error) {
	app, err := s.repo.GetApplication(ctx, name)
	if err != nil {
		return nil, err
	}

	return app, nil
}

func (s *svc) CreateApplication(ctx context.Context, param CreateApplicationParam) (*appent.Application, error) {
	app := &appent.Application{
		ID:          0,
		Description: param.Description,
		Name:        param.Name,
		Owners:      param.Owners,
	}

	err := s.validateApplication(ctx, app)
	if err != nil {
		return nil, err
	}

	return s.repo.CreateApplication(ctx, app)
}

func (s *svc) UpdateApplication(ctx context.Context, name string, param UpdateApplicationParam) (*appent.Application, error) {
	app, err := s.repo.GetApplication(ctx, name)
	if err != nil {
		return nil, err
	}

	if param.Description != nil {
		app.Description = *param.Description
	}
	if param.Owners != nil {
		app.Owners = *param.Owners
	}

	err = s.validateApplication(ctx, app)
	if err != nil {
		return nil, err
	}

	return s.repo.UpdateApplication(ctx, app)
}

func (s *svc) DeleteApplication(ctx context.Context, name string) error {
	return s.repo.DeleteApplication(ctx, name)
}

func (s *svc) validateApplication(ctx context.Context, app *appent.Application) error {
	for _, owner := range app.Owners {
		_, err := s.userSvc.GetUser(ctx, &userv1.GetUserRequest{Email: owner})
		if err != nil {
			return fmt.Errorf("get owner '%s' from user service failed: %s", owner, err)
		}
	}

	return nil
}
