package api

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/tidbcloud/api-gateway/ui/internal/controller"
	"github.com/tidbcloud/api-gateway/ui/internal/dao"
	"github.com/tidbcloud/api-gateway/ui/internal/util"
	"sigs.k8s.io/yaml"
)

type RouteParam struct {
	Host        string   `json:"host"`
	Path        string   `json:"path"`
	Namespace   string   `json:"namespace"`
	ServiceName string   `json:"service_name"`
	ServicePort string   `json:"service_port"`
	TestCases   []string `json:"test_cases"`
}

func (rp *RouteParam) ToRouteModel(label int) util.RouteModel {
	servicePort, _ := strconv.Atoi(rp.ServicePort)
	return util.RouteModel{
		Label:       label,
		Host:        rp.Host,
		Path:        rp.Path,
		Namespace:   rp.Namespace,
		ServiceName: rp.ServiceName,
		ServicePort: servicePort,
		TestCases:   rp.TestCases,
	}
}

func CreateRoute(ctx *gin.Context) {
	param := new(RouteParam)
	err := ctx.Bind(param)
	if err != nil {
		AmisError(ctx, fmt.Errorf("bind failed: %s", err))
		return
	}

	routes, err := dao.ListRoutesByHost(param.Host)
	if err != nil {
		AmisError(ctx, fmt.Errorf("list routes failed: %s", err))
		return
	}
	routeModels := make(util.RouteModels, 0, 10)
	routeModels = append(routeModels, param.ToRouteModel(0))
	for i, dr := range routes {
		routeModels = append(routeModels, dr.ToRouteModel(i+1))
	}
	if err := routeModels.Validate(); err != nil {
		AmisError(ctx, err)
		return
	}

	err = dao.CreateRoute(
		param.Host,
		param.Path,
		param.Namespace,
		param.ServiceName,
		param.ServicePort,
		strings.Join(param.TestCases, ","),
	)
	if err != nil {
		AmisError(ctx, fmt.Errorf("insert db failed: %s", err))
		return
	}

	// TODO 检查是否有更新
	ingresses := controller.BuildIngresses(routeModels)
	resJson, _ := json.Marshal(ingresses)
	yml, _ := yaml.JSONToYAML(resJson)
	err = controller.DeployIngresses(ingresses)
	if err != nil {
		AmisError(ctx, fmt.Errorf("deploy ingress failed: %s", err))
		return
	}

	err = dao.CreateNewVersion(param.Host, string(yml))
	if err != nil {
		AmisError(ctx, fmt.Errorf("create new version failed: %s", err))
		return
	}

	AmisSuccess(ctx, nil)
}

type RouteResp struct {
	ID          int      `json:"id"`
	Host        string   `json:"host"`
	Path        string   `json:"path"`
	Namespace   string   `json:"namespace"`
	ServiceName string   `json:"service_name"`
	ServicePort string   `json:"service_port"`
	TestCases   []string `json:"test_cases"`
}

func ListRoutes(ctx *gin.Context) {
	routes, err := dao.ListRoutes()
	if err != nil {
		AmisError(ctx, fmt.Errorf("list route from db failed: %s", err))
		return
	}
	resps := make([]RouteResp, 0, 10)
	for _, route := range routes {
		resps = append(resps, RouteResp{
			ID:          route.ID,
			Host:        route.Host,
			Path:        route.Path,
			Namespace:   route.Namespace,
			ServiceName: route.ServiceName,
			ServicePort: strconv.Itoa(route.ServicePort),
			TestCases:   strings.Split(route.TestCases, ","),
		})
	}
	AmisSuccess(ctx, resps)
}

func GetRoute(ctx *gin.Context) {
	routeID := ctx.Param("routeID")
	id, err := strconv.Atoi(routeID)
	if err != nil {
		AmisError(ctx, fmt.Errorf("invalid route id: %s", routeID))
		return
	}
	route, err := dao.GetRoute(id)
	if err != nil {
		AmisError(ctx, fmt.Errorf("get route failed: %s", err))
		return
	}
	resp := RouteResp{
		ID:          route.ID,
		Host:        route.Host,
		Path:        route.Path,
		Namespace:   route.Namespace,
		ServiceName: route.ServiceName,
		ServicePort: strconv.Itoa(route.ServicePort),
		TestCases:   strings.Split(route.TestCases, ","),
	}
	AmisSuccess(ctx, resp)
}

func UpdateRoute(ctx *gin.Context) {
	routeID := ctx.Param("routeID")
	id, err := strconv.Atoi(routeID)
	if err != nil {
		AmisError(ctx, fmt.Errorf("invalid route id: %s", routeID))
		return
	}
	param := new(RouteParam)
	err = ctx.Bind(param)
	if err != nil {
		AmisError(ctx, fmt.Errorf("bind failed: %s", err))
		return
	}

	routes, err := dao.ListRoutesByHost(param.Host)
	if err != nil {
		AmisError(ctx, fmt.Errorf("list routes failed: %s", err))
		return
	}
	routeModels := make(util.RouteModels, 0, 10)
	routeModels = append(routeModels, param.ToRouteModel(0))
	for i, dr := range routes {
		// TODO: tricky
		if dr.ID == id {
			continue
		}
		routeModels = append(routeModels, dr.ToRouteModel(i+1))
	}
	if err := routeModels.Validate(); err != nil {
		AmisError(ctx, err)
		return
	}

	err = dao.UpdateRoute(
		id,
		param.Host,
		param.Path,
		param.Namespace,
		param.ServiceName,
		param.ServicePort,
		strings.Join(param.TestCases, ","),
	)
	if err != nil {
		AmisError(ctx, fmt.Errorf("update route from db failed: %s", err))
		return
	}

	// TODO 检查是否有更新
	ingresses := controller.BuildIngresses(routeModels)
	resJson, _ := json.Marshal(ingresses)
	yml, _ := yaml.JSONToYAML(resJson)
	err = controller.DeployIngresses(ingresses)
	if err != nil {
		AmisError(ctx, fmt.Errorf("deploy ingress failed: %s", err))
		return
	}

	err = dao.CreateNewVersion(param.Host, string(yml))
	if err != nil {
		AmisError(ctx, fmt.Errorf("create new version failed: %s", err))
		return
	}

	AmisSuccess(ctx, nil)
}

func DeleteRoute(ctx *gin.Context) {
	routeID := ctx.Param("routeID")
	id, err := strconv.Atoi(routeID)
	if err != nil {
		AmisError(ctx, fmt.Errorf("invalid route id: %s", routeID))
		return
	}
	err = dao.DeleteRoute(id)
	if err != nil {
		AmisError(ctx, fmt.Errorf("delete route from db failed: %s", err))
		return
	}
	AmisSuccess(ctx, nil)
}
