package tcms

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/tidbcloud/pov-service/internal/model"
)

type testbedBase struct {
	Name       string
	ID         int64
	PlanExecID int64
}

type clusterBase struct {
	testbedBase
	Host string
	Port int
}

type CloudCluster struct {
	clusterBase
	TiDBCloudOrg       string
	TiDBCloudProject   string
	TiDBCloudClusterID string
	MinRU              int64
	MaxRU              int64
	ClusterType        model.ClusterType
}

func NewCreateCloudClusterOutput(
	task WorkflowTask,
) (CloudCluster, error) {
	ret := CloudCluster{}

	outputs := task.Outputs
	testbedItemMap, ok := outputs["testbed_items"].(map[string]any)
	if !ok {
		return ret, fmt.Errorf("invalid testbed items")
	}
	testbedItems, ok := testbedItemMap["object"].([]any)
	if !ok {
		return ret, fmt.Errorf("invalid testbed items")
	}

	for _, item := range testbedItems {
		testbedItem, ok := item.(map[string]any)
		if !ok {
			return ret, fmt.Errorf("invalid testbed item")
		}
		if value, ok := testbedItem["type"].(string); ok {
			switch value {
			case "TIDBCLOUD_NEXTGEN_CLUSTER":
				return parseNGOutput(task)
			case "TIDBCLOUD_SERVERLESS":
				return parseEssentialOutput(task)
			}
		}
	}

	return ret, fmt.Errorf("invalid testbed items")
}

func parseNGOutput(
	task WorkflowTask,
) (ret CloudCluster, err error) {
	testbedBase, err := parseBaseOutput(task)
	if err != nil {
		return
	}
	ret.testbedBase = testbedBase
	ret.ClusterType = model.ClusterTypeTiDBCloudPremium

	outputs := task.Outputs
	tc, ok := outputs["tc"].(map[string]any)
	if !ok {
		err = fmt.Errorf("invalid tc output")
		return
	}

	privateEndpointsAny, ok := tc["object"].(map[string]any)["private_endpoints"]
	if ok {
		privateEndpoints, ok := privateEndpointsAny.(map[string]any)
		if !ok {
			err = fmt.Errorf("'private_endpoints' is not a map")
			return
		}

		keyspace1Any, ok := privateEndpoints["keyspace1"]
		if !ok {
			err = fmt.Errorf("missing 'keyspace1' key in private_endpoints")
			return
		}
		dsn, ok := keyspace1Any.(string)
		if !ok {
			err = fmt.Errorf("'keyspace1' is not a string")
			return
		}

		// Parse dsn to extract host and port
		// Expected format: root:@tcp(host:port)
		start := strings.Index(dsn, "(")
		end := strings.Index(dsn, ")")
		var host string
		var port int
		if start == -1 || end == -1 || start >= end {
			err = fmt.Errorf("invalid dsn format: %s", dsn)
			return
		}
		hostPortStr := dsn[start+1 : end]
		parts := strings.Split(hostPortStr, ":")
		if len(parts) != 2 {
			err = fmt.Errorf("invalid dsn format: %s", dsn)
			return
		}
		host = parts[0]
		parsedPort, nerr := strconv.Atoi(parts[1])
		if nerr != nil {
			err = fmt.Errorf("invalid dsn format: %s", dsn)
			return
		}
		port = parsedPort

		ret.Host = host
		ret.Port = port
	}

	// get minRU and maxRU from tc
	clusterRus, ok := tc["object"].(map[string]any)["cluster_rus"]
	if !ok {
		err = fmt.Errorf("missing 'cluster_rus' key in tc")
		return
	}
	clusterRusMap, ok := clusterRus.(map[string]any)
	if !ok {
		err = fmt.Errorf("'cluster_rus' is not a map")
		return
	}

	// Get keyspace1 RUs
	keyspace1Rus, ok := clusterRusMap["keyspace1"]
	if !ok {
		err = fmt.Errorf("missing 'keyspace1' key in cluster_rus")
		return
	}
	keyspaceRus, ok := keyspace1Rus.(map[string]any)
	if !ok {
		err = fmt.Errorf("'keyspace1' is not a map")
		return
	}

	minRU, ok := keyspaceRus["min"]
	if !ok {
		err = fmt.Errorf("missing 'min' key in cluster_rus for keyspace1")
		return
	}
	ret.MinRU = int64(minRU.(float64))

	maxRU, ok := keyspaceRus["max"]
	if !ok {
		err = fmt.Errorf("missing 'max' key in cluster_rus for keyspace1")
		return
	}
	ret.MaxRU = int64(maxRU.(float64))

	// get clusterID from tc
	clusterIds, ok := tc["object"].(map[string]any)["cluster_ids"]
	if !ok {
		err = fmt.Errorf("missing 'cluster_ids' key in tc")
		return
	}
	clusterIdsMap, ok := clusterIds.(map[string]any)
	if !ok {
		err = fmt.Errorf("'cluster_ids' is not a map")
		return
	}
	clusterId, ok := clusterIdsMap["keyspace1"]
	if !ok {
		err = fmt.Errorf("missing 'keyspace1' key in cluster_ids")
		return
	}
	ret.TiDBCloudClusterID = clusterId.(string)
	tenantId, ok := tc["object"].(map[string]any)["tenant_id"]
	if !ok {
		err = fmt.Errorf("missing 'tenant_id' key in tc")
		return
	}
	ret.TiDBCloudOrg = tenantId.(string)

	return
}

func parseEssentialOutput(task WorkflowTask) (ret CloudCluster, err error) {
	testbedBase, err := parseBaseOutput(task)
	if err != nil {
		return
	}
	ret.testbedBase = testbedBase
	ret.ClusterType = model.ClusterTypeTiDBCloudEssential

	outputs := task.Outputs
	tc, ok := outputs["tc"].(map[string]any)
	if !ok {
		err = fmt.Errorf("invalid tc output")
		return
	}

	privateEndpointAny, ok := tc["object"].(map[string]any)["private_endpoint"]
	if ok {
		dsn, ok := privateEndpointAny.(string)
		if !ok {
			err = fmt.Errorf("'private_endpoint' is not a string")
			return
		}

		// Parse dsn to extract host and port
		// Expected format: root:@tcp(host:port)
		start := strings.Index(dsn, "(")
		end := strings.Index(dsn, ")")
		var host string
		var port int
		if start == -1 || end == -1 || start >= end {
			err = fmt.Errorf("invalid dsn format: %s", dsn)
			return
		}
		hostPortStr := dsn[start+1 : end]
		parts := strings.Split(hostPortStr, ":")
		if len(parts) != 2 {
			err = fmt.Errorf("invalid dsn format: %s", dsn)
			return
		}
		host = parts[0]
		parsedPort, nerr := strconv.Atoi(parts[1])
		if nerr != nil {
			err = fmt.Errorf("invalid dsn format: %s", dsn)
			return
		}
		port = parsedPort

		ret.Host = host
		ret.Port = port

	}

	clusterID, ok := tc["object"].(map[string]any)["cluster_id"]
	if !ok {
		err = fmt.Errorf("missing 'cluster_id' key in tc")
		return
	}
	ret.TiDBCloudClusterID = clusterID.(string)

	tenantId, ok := tc["object"].(map[string]any)["tenant_id"]
	if !ok {
		err = fmt.Errorf("missing 'tenant_id' key in tc")
		return
	}
	ret.TiDBCloudOrg = tenantId.(string)

	projectID, ok := tc["object"].(map[string]any)["project_id"]
	if !ok {
		err = fmt.Errorf("missing 'project_id' key in tc")
		return
	}
	ret.TiDBCloudProject = projectID.(string)

	clusterRu, ok := tc["object"].(map[string]any)["cluster_ru"].(map[string]any)
	if !ok {
		err = fmt.Errorf("missing 'cluster_ru' key in tc")
		return
	}

	minRU, ok := clusterRu["min"]
	if !ok {
		err = fmt.Errorf("missing 'min' key in cluster_rus for keyspace1")
		return
	}
	ret.MinRU = int64(minRU.(float64))

	maxRU, ok := clusterRu["max"]
	if !ok {
		err = fmt.Errorf("missing 'max' key in cluster_rus for keyspace1")
		return
	}
	ret.MaxRU = int64(maxRU.(float64))

	return
}

func parseBaseOutput(task WorkflowTask) (testbedBase, error) {
	base := testbedBase{}

	outputs := task.Outputs
	testbedName, ok := outputs["testbed_name"].(map[string]any)
	if !ok {
		return base, fmt.Errorf("invalid testbed name")
	}
	base.Name = testbedName["string"].(string)

	testbedId := outputs["testbed_id"].((map[string]any))
	base.ID = int64(testbedId["int"].(float64))

	stateOutput := task.StateOutput
	base.PlanExecID = int64(stateOutput["planExecutionID"].(float64))

	return base, nil
}
