package application

import (
	"fmt"

	"github.com/tidbcloud/devops/api"
	appapi "github.com/tidbcloud/devops/api/application"
	ctrl "github.com/tidbcloud/devops/pkg/controller"
	appsvc "github.com/tidbcloud/devops/pkg/service/application"

	"github.com/gin-gonic/gin"
	"go.uber.org/dig"
)

type ControllerParam struct {
	dig.In

	ApplicationService appsvc.ApplicationService
}

type ApplicationController struct {
	appSvc appsvc.ApplicationService
}

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

func NewApplicationController(p ControllerParam) ctrl.Controller {
	return &ApplicationController{
		appSvc: p.ApplicationService,
	}
}

func (c *ApplicationController) RegisterAPI(suite *ctrl.Suite) error {
	g := suite.Router.Group("/application")
	{
		g.GET("/applications", c.ListApplications)
		g.POST("/applications", c.CreateApplication)
		g.GET("/applications/:name", c.GetApplication)
		g.PUT("/applications/:name", c.UpdateApplication)
		g.DELETE("/applications/:name", c.DeleteApplication)
	}

	return nil
}

// ListApplication
//
//	@Id				ApplicationControllerListApplication
//	@Description	list applications
//	@Tags			ApplicationController
//	@Accept			json
//	@Produce		json
//	@Success		200	{object}	appapi.ListApplicationResp
//	@Router			/api/v1/devops/application/applications [get]
func (c *ApplicationController) ListApplications(ctx *gin.Context) {
	param := appsvc.ListApplicationsParam{}
	apps, total, err := c.appSvc.ListApplications(ctx, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := appapi.ListApplicationResp{
		Applications: apps,
		Total:        total,
	}
	ctrl.Success(ctx, resp)
}

// GetApplication
//
//	@Id				ApplicationControllerGetApplication
//	@Description	get a application
//	@Tags			ApplicationController
//	@Accept			json
//	@Produce		json
//	@Param			name	path		string	true	"application name"
//	@Success		200		{object}	appapi.GetApplicationResp
//	@Router			/api/v1/devops/application/applications/{name} [get]
func (c *ApplicationController) GetApplication(ctx *gin.Context) {
	name := ctx.Param("name")
	if name == "" {
		ctrl.FailWithCode(ctx, fmt.Errorf("app_name is required"), api.ErrCodeInvalidArgument)
		return
	}

	app, err := c.appSvc.GetApplication(ctx, name)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := appapi.GetApplicationResp{
		Application: app,
	}
	ctrl.Success(ctx, resp)
}

// CreateApplication
//
//	@Id				ApplicationControllerCreateApplication
//	@Description	create a application
//	@Tags			ApplicationController
//	@Accept			json
//	@Produce		json
//	@Param			req	body		appapi.CreateApplicationReq	true	"request"
//	@Success		200	{object}	appapi.CreateApplicationResp
//	@Router			/api/v1/devops/application/applications [post]
func (c *ApplicationController) CreateApplication(ctx *gin.Context) {
	req := &appapi.CreateApplicationReq{}
	err := ctx.BindJSON(req)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("invalid request body: %w", err), api.ErrCodeInvalidArgument)
		return
	}

	param := appsvc.CreateApplicationParam{
		Name:        req.Name,
		Description: req.Description,
		Owners:      req.Owners,
	}
	app, err := c.appSvc.CreateApplication(ctx, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := appapi.CreateApplicationResp{
		ApplicationID: app.ID,
	}
	ctrl.Success(ctx, resp)
}

// UpdateApplication
//
//	@Id				ApplicationControllerUpdateApplication
//	@Description	update a application
//	@Tags			ApplicationController
//	@Accept			json
//	@Produce		json
//	@Param			name	path		string						true	"application name"
//	@Param			req		body		appapi.CreateApplicationReq	true	"request"
//	@Success		200		{object}	api.BaseResp
//	@Router			/api/v1/devops/application/applications/{name} [put]
func (c *ApplicationController) UpdateApplication(ctx *gin.Context) {
	name := ctx.Param("name")
	if name == "" {
		ctrl.FailWithCode(ctx, fmt.Errorf("app_name is required"), api.ErrCodeInvalidArgument)
		return
	}

	req := &appapi.UpdateApplicationReq{}
	err := ctx.BindJSON(req)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("invalid request body: %w", err), api.ErrCodeInvalidArgument)
		return
	}

	param := appsvc.UpdateApplicationParam{
		Description: req.Description,
		Owners:      req.Owners,
	}
	_, err = c.appSvc.UpdateApplication(ctx, name, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	ctrl.Success(ctx, nil)
}

// DeleteApplication
//
//	@Id				ApplicationControllerDeleteApplication
//	@Description	delete a application
//	@Tags			ApplicationController
//	@Accept			json
//	@Produce		json
//	@Param			name	path		string	true	"application name"
//	@Success		200		{object}	api.BaseResp
//	@Router			/api/v1/devops/application/applications/{name} [delete]
func (c *ApplicationController) DeleteApplication(ctx *gin.Context) {
	name := ctx.Param("name")
	if name == "" {
		ctrl.FailWithCode(ctx, fmt.Errorf("app_name is required"), api.ErrCodeInvalidArgument)
		return
	}

	err := c.appSvc.DeleteApplication(ctx, name)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	ctrl.Success(ctx, nil)
}
