package project

import (
	"fmt"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/go-logr/logr"
	userapiv1 "github.com/tidbcloud/aisupport/packages/sdk-go/contracts/api/core-server/user/v1"
	"go.uber.org/dig"

	"github.com/tidbcloud/devops/api"
	projectapi "github.com/tidbcloud/devops/api/project"
	opent "github.com/tidbcloud/devops/entity/operation"
	ctrl "github.com/tidbcloud/devops/pkg/controller"
	"github.com/tidbcloud/devops/pkg/infra/config"
	"github.com/tidbcloud/devops/pkg/infra/consts"
	opsvc "github.com/tidbcloud/devops/pkg/service/operation"
	"github.com/tidbcloud/devops/pkg/service/operation/actions/projectaction"
	opsvcins "github.com/tidbcloud/devops/pkg/service/operation/instance"
	projectsvc "github.com/tidbcloud/devops/pkg/service/project"
)

type ControllerParam struct {
	dig.In

	Conf             *config.RootConfig
	Logger           logr.Logger
	ProjectService   projectsvc.ProjectServiceAPI
	OperationService opsvc.OperationServiceAPI
	//AuditService      auditsvc.AuditServiceAPI
}

type ProjectController struct {
	logger         logr.Logger
	projectService projectsvc.ProjectServiceAPI
	opSvc          opsvc.OperationServiceAPI
	//auditSvc          auditsvc.AuditServiceAPI
}

func (c *ProjectController) Name() string {
	return "ProjectController"
}

func NewProjectController(p ControllerParam) ctrl.Controller {
	return &ProjectController{
		logger:         p.Logger.WithName("ProjectController"),
		projectService: p.ProjectService,
		opSvc:          p.OperationService,
	}
}

func (c *ProjectController) RegisterAPI(suite *ctrl.Suite) error {
	r := suite.Router
	group := r.Group("/tenants/:tenant_id/projects")
	group.Use(ctrl.TenantScopeMiddleware())
	{
		group.GET("/:project_id/vpc", c.ListVPCs)
		group.POST("/:project_id/vpc/:vpc_id/cidr", c.AddSecondaryCIDR)
	}
	return nil
}

// ListVPCs
//
//	@Id				ProjectControllerListVPCs
//	@Description	list vpcs of the project
//	@Tags			ProjectController
//	@Accept			json
//	@Produce		json
//	@Param			tenant_id	path		string					true	"tenant id"
//	@Param			project_id	path		string					true	"project id"
//	@Param			req			query		projectapi.ListVPCsReq	false	"request param"
//	@Success		200			{object}	projectapi.ListVPCsResp
//	@Router			/api/v1/devops/tenants/{tenant_id}/projects/{project_id}/vpc [get]
func (c *ProjectController) ListVPCs(ctx *gin.Context) {
	oid, err := strconv.ParseUint(ctx.Param("tenant_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("tenant_id is not a int"), api.ErrCodeInvalidArgument)
		return
	}
	pid, err := strconv.ParseUint(ctx.Param("project_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("project_id is not a int"), api.ErrCodeInvalidArgument)
		return
	}
	req := &projectapi.ListVPCsReq{}
	err = ctx.ShouldBindQuery(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	param := &projectsvc.ListVPCsRequest{
		OrgID:     oid,
		ProjectID: pid,
		Provider:  req.Provider,
		Region:    req.Region,
	}

	resp, err := c.projectService.ListVPCs(ctx, param)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("list vpcs error: %s", err), api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, projectapi.ListVPCsResp{
		VPCs: projectapi.BuildProjectVPCs(resp.Vpcs),
	})
}

// AddSecondaryCIDR
//
//	@Id				ProjectControllerAddSecondaryCIDR
//	@Description	add a secondary cidr for a project
//	@Tags			ProjectController
//	@Accept			json
//	@Produce		json
//	@Param			tenant_id	path		string							true	"tenant id"
//	@Param			project_id	path		string							true	"project id"
//	@Param			vpc_id		path		string							true	"vpc id"
//	@Param			req			body		projectapi.AddSecondaryCIDRReq	true	"request body"
//	@Success		200			{object}	projectapi.CommonResp
//	@Router			/api/v1/devops/tenants/{tenant_id}/projects/{project_id}/vpc/{vpc_id}/cidr [post]
func (c *ProjectController) AddSecondaryCIDR(ctx *gin.Context) {
	user, ok := ctx.Get(consts.CtxUserProfile)
	if !ok {
		ctrl.FailWithCode(ctx, fmt.Errorf("user profile not found"), api.ErrCodeUnauthenticated)
		return
	}
	userProfile := user.(*userapiv1.PrincipalProfile)

	oid, err := strconv.ParseUint(ctx.Param("tenant_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("tenant_id is not a int"), api.ErrCodeInvalidArgument)
		return
	}
	pid, err := strconv.ParseUint(ctx.Param("project_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("project_id is not a int"), api.ErrCodeInvalidArgument)
		return
	}
	vid := ctx.Param("vpc_id")

	req := &projectapi.AddSecondaryCIDRReq{}
	err = ctx.ShouldBindJSON(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}
	createOpParam := opsvc.CreateInstanceParam{
		Type:      opent.OperationTargetProject,
		Operation: "AddSecondaryCIDR",
		Applicant: userProfile.UserEmail,
		Reason:    req.Reason,
		Params: map[opent.ParamName]string{
			opent.ParamTenantID:      strconv.FormatUint(oid, 10),
			opent.ParamProjectID:     strconv.FormatUint(pid, 10),
			opent.ParamVPCID:         vid,
			opent.ParamProvider:      req.Provider,
			opent.ParamRegion:        req.Region,
			opent.ParamSecondaryCIDR: req.SecondaryCIDR,
		},

		Actions: []opsvcins.CreateInstanceActionParam{
			{
				Name:        projectaction.ActionAddSecondaryCidr.Name,
				StartPolicy: opent.ActionStartPolicyManual,
			},
		},
	}

	insID, err := c.opSvc.CreateInstance(ctx, createOpParam)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &projectapi.CommonResp{InstanceID: insID}
	ctrl.Success(ctx, resp)
}
