// Package services provides mock implementations of dataplane-manager gRPC services.
package services

import (
	"context"
	"encoding/base64"
	"fmt"
	"os"

	dpmv1 "github.com/tidbcloud/cluster-service-ng/api/dataplane-manager/v1"
	"github.com/tidbcloud/cluster-service-ng/hack/mock-dpm/store"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"
)

// DataPlaneService implements the DataPlaneServiceServer interface.
type DataPlaneService struct {
	dpmv1.UnimplementedDataPlaneServiceServer
	store *store.MemoryStore
}

// NewDataPlaneService creates a new DataPlaneService.
func NewDataPlaneService(s *store.MemoryStore) *DataPlaneService {
	return &DataPlaneService{store: s}
}

// ListNodePoolConfigs returns available node pool configurations.
func (s *DataPlaneService) ListNodePoolConfigs(ctx context.Context, req *dpmv1.ListNodePoolConfigsRequest) (*dpmv1.ListNodePoolConfigsResponse, error) {
	configs := s.store.GetNodePoolConfigs()
	return &dpmv1.ListNodePoolConfigsResponse{
		NodePoolConfigs: configs,
	}, nil
}

// CreateDataPlane creates a new dataplane (K8s cluster).
func (s *DataPlaneService) CreateDataPlane(ctx context.Context, req *dpmv1.CreateDataPlaneRequest) (*dpmv1.DataPlane, error) {
	if req.DataPlaneId == "" {
		return nil, status.Error(codes.InvalidArgument, "data_plane_id is required")
	}
	if req.DataPlane == nil {
		return nil, status.Error(codes.InvalidArgument, "data_plane is required")
	}

	if existing, ok := s.store.GetDataPlane(req.DataPlaneId); ok {
		return existing, nil
	}

	var vpcCidr string
	if req.DataPlane.Vpc != nil {
		vpcCidr = req.DataPlane.Vpc.GetCidr()
	}
	if vpcCidr == "" {
		vpcCidr = "10.0.0.0/16"
	}

	dp := &dpmv1.DataPlane{
		Id:        req.DataPlaneId,
		AccountId: req.DataPlane.AccountId,
		Tags:      req.DataPlane.Tags,
		Vpc: &dpmv1.DataPlane_Vpc{
			Cidr:                vpcCidr,
			NatIps:              []string{"10.0.0.1"},
			AvailabilityZones:   []string{"us-east-1a"},
			AvailabilityZoneIds: []string{"use1-az1"},
		},
		Kubernetes: &dpmv1.DataPlane_Kubernetes{
			Version:     "1.28",
			Kubeconfig:  generateMockKubeconfig(req.DataPlaneId),
			Name:        fmt.Sprintf("mock-k8s-%s", req.DataPlaneId),
			ServiceCidr: "10.96.0.0/12",
		},
		ApiGateway: fmt.Sprintf("https://api.%s.mock.tidbcloud.com", req.DataPlaneId),
		NodeRole:   fmt.Sprintf("arn:aws:iam::123456789012:role/mock-node-role-%s", req.DataPlaneId),
		NodeZones:  []string{"us-east-1a"},
		Status:     dpmv1.DataPlane_Active,
		AuthToken:  "mock-auth-token-12345",
		Domain:     fmt.Sprintf("%s.clusters.mock.tidbcloud.com", req.DataPlaneId),
	}

	s.store.SetDataPlane(dp)
	s.ensureDefaultNodePools(req.DataPlaneId)
	return dp, nil
}

func (s *DataPlaneService) ensureDefaultNodePools(dataplaneID string) {
	pools := s.store.GetNodePools(dataplaneID)
	if len(pools) > 0 {
		return
	}

	defaultPools := []*dpmv1.NodePool{
		{InstanceType: "m5.xlarge", CpuCore: 4, MemoryGi: 8, AvailableCpuMilliCore: 3500, AvailableMemoryMi: 7168, NodeType: dpmv1.NodeType_Dedicated, K8SVersion: "1.28"},
	}

	for _, np := range defaultPools {
		s.store.AddNodePool(dataplaneID, np)
	}
}

// GetDataPlane retrieves a dataplane by ID.
func (s *DataPlaneService) GetDataPlane(ctx context.Context, req *dpmv1.GetDataPlaneRequest) (*dpmv1.DataPlane, error) {
	if req.DataPlaneId == "" {
		return nil, status.Error(codes.InvalidArgument, "data_plane_id is required")
	}

	// First check if it exists in store
	if dp, ok := s.store.GetDataPlane(req.DataPlaneId); ok {
		return dp, nil
	}

	// Auto-create a mock dataplane for any requested ID (simulates existing infrastructure)
	dp := &dpmv1.DataPlane{
		Id:        req.DataPlaneId,
		AccountId: "000000000000",
		Tags:      map[string]string{"mock": "true"},
		Vpc: &dpmv1.DataPlane_Vpc{
			Cidr:                "10.0.0.0/16",
			NatIps:              []string{"10.0.0.1"},
			AvailabilityZones:   []string{"us-east-1a"},
			AvailabilityZoneIds: []string{"use1-az1"},
		},
		Kubernetes: &dpmv1.DataPlane_Kubernetes{
			Version:     "1.28",
			Kubeconfig:  generateMockKubeconfig(req.DataPlaneId),
			Name:        fmt.Sprintf("mock-k8s-%s", req.DataPlaneId),
			ServiceCidr: "10.96.0.0/12",
		},
		ApiGateway: fmt.Sprintf("https://api.%s.mock.tidbcloud.com", req.DataPlaneId),
		NodeRole:   fmt.Sprintf("arn:aws:iam::123456789012:role/mock-node-role-%s", req.DataPlaneId),
		NodeZones:  []string{"us-east-1a"},
		Status:     dpmv1.DataPlane_Active,
		AuthToken:  "mock-auth-token-12345",
		Domain:     fmt.Sprintf("%s.clusters.mock.tidbcloud.com", req.DataPlaneId),
	}

	s.store.SetDataPlane(dp)
	s.ensureDefaultNodePools(req.DataPlaneId)
	return dp, nil
}

// ListDataPlanes returns all dataplanes.
func (s *DataPlaneService) ListDataPlanes(ctx context.Context, req *dpmv1.ListDataPlanesRequest) (*dpmv1.ListDataPlanesResponse, error) {
	return &dpmv1.ListDataPlanesResponse{
		DataPlanes: s.store.ListDataPlanes(),
	}, nil
}

// DeleteDataPlane deletes a dataplane.
func (s *DataPlaneService) DeleteDataPlane(ctx context.Context, req *dpmv1.DeleteDataPlaneRequest) (*emptypb.Empty, error) {
	if req.DataPlaneId == "" {
		return nil, status.Error(codes.InvalidArgument, "data_plane_id is required")
	}
	s.store.DeleteDataPlane(req.DataPlaneId)
	return &emptypb.Empty{}, nil
}

// UpgradeDataPlane upgrades the K8s version.
func (s *DataPlaneService) UpgradeDataPlane(ctx context.Context, req *dpmv1.UpgradeDataPlaneRequest) (*emptypb.Empty, error) {
	dp, ok := s.store.GetDataPlane(req.DataPlaneId)
	if !ok {
		return nil, status.Error(codes.NotFound, "dataplane not found")
	}
	dp.Kubernetes.Version = req.K8SVersion
	s.store.SetDataPlane(dp)
	return &emptypb.Empty{}, nil
}

// CreateNodePool creates a new node pool in the dataplane.
func (s *DataPlaneService) CreateNodePool(ctx context.Context, req *dpmv1.CreateNodePoolRequest) (*dpmv1.NodePool, error) {
	if req.DataPlaneId == "" {
		return nil, status.Error(codes.InvalidArgument, "data_plane_id is required")
	}
	if req.NodePool == nil {
		return nil, status.Error(codes.InvalidArgument, "node_pool is required")
	}

	np := req.NodePool
	if np.Version == "" {
		np.Version = "v1"
	}

	s.store.AddNodePool(req.DataPlaneId, np)
	return np, nil
}

// ListNodePools returns all node pools for a dataplane.
func (s *DataPlaneService) ListNodePools(ctx context.Context, req *dpmv1.ListNodePoolsRequest) (*dpmv1.ListNodePoolsResponse, error) {
	if req.DataPlaneId == "" {
		return nil, status.Error(codes.InvalidArgument, "data_plane_id is required")
	}

	pools := s.store.GetNodePools(req.DataPlaneId)
	return &dpmv1.ListNodePoolsResponse{
		NodePools: pools,
	}, nil
}

// DeleteNodePool deletes node pools matching criteria.
func (s *DataPlaneService) DeleteNodePool(ctx context.Context, req *dpmv1.DeleteNodePoolRequest) (*emptypb.Empty, error) {
	if req.DataPlaneId == "" {
		return nil, status.Error(codes.InvalidArgument, "data_plane_id is required")
	}
	s.store.DeleteNodePool(req.DataPlaneId, req.NodePoolId)
	return &emptypb.Empty{}, nil
}

// ListK8sInformation returns K8s information for dataplanes with a specific version.
func (s *DataPlaneService) ListK8sInformation(ctx context.Context, req *dpmv1.ListK8SInformationRequest) (*dpmv1.K8SInformationList, error) {
	return &dpmv1.K8SInformationList{
		K8SInfos: []*dpmv1.K8SInformation{},
		Total:    0,
	}, nil
}

// GetK8sInformation returns K8s information for a specific dataplane.
func (s *DataPlaneService) GetK8sInformation(ctx context.Context, req *dpmv1.GetK8SInformationRequest) (*dpmv1.K8SInformation, error) {
	dp, ok := s.store.GetDataPlane(req.DataPlaneId)
	if !ok {
		return nil, status.Error(codes.NotFound, "dataplane not found")
	}

	return &dpmv1.K8SInformation{
		DataPlaneId:    req.DataPlaneId,
		CloudProvider:  "aws",
		CloudAccount:   dp.AccountId,
		Region:         "us-east-1",
		TargetVersion:  dp.Kubernetes.Version,
		MaxSkewVersion: 0,
		ControlPlane: &dpmv1.K8SControlPlane{
			Name:    dp.Kubernetes.Name,
			Version: dp.Kubernetes.Version,
		},
		Addons:                      []*dpmv1.K8SAddon{},
		NodePools:                   []*dpmv1.K8SNodePool{},
		ExpectedWorkerNodePoolCount: 0,
	}, nil
}

// SetKongAuditLogSetting sets Kong audit log settings.
func (s *DataPlaneService) SetKongAuditLogSetting(ctx context.Context, req *dpmv1.SetKongAuditLogSettingRequest) (*emptypb.Empty, error) {
	return &emptypb.Empty{}, nil
}

func generateMockKubeconfig(dataplaneID string) string {
	var rawKubeconfig []byte

	kubeconfigPath := os.Getenv("KIND_KUBECONFIG")
	if kubeconfigPath != "" {
		if kubeconfig, err := os.ReadFile(kubeconfigPath); err == nil {
			rawKubeconfig = kubeconfig
		}
	}

	if rawKubeconfig == nil {
		home, _ := os.UserHomeDir()
		kindConfigPath := home + "/.kube/kind-config-csng-dev"
		if kubeconfig, err := os.ReadFile(kindConfigPath); err == nil {
			rawKubeconfig = kubeconfig
		}
	}

	if rawKubeconfig == nil {
		home, _ := os.UserHomeDir()
		defaultConfigPath := home + "/.kube/config"
		if kubeconfig, err := os.ReadFile(defaultConfigPath); err == nil {
			rawKubeconfig = kubeconfig
		}
	}

	if rawKubeconfig == nil {
		rawKubeconfig = []byte(fmt.Sprintf(`apiVersion: v1
kind: Config
clusters:
- cluster:
    server: https://127.0.0.1:6443
    insecure-skip-tls-verify: true
  name: kind-csng-dev
contexts:
- context:
    cluster: kind-csng-dev
    user: kind-csng-dev
  name: kind-csng-dev
current-context: kind-csng-dev
users:
- name: kind-csng-dev
  user:
    token: mock-token-for-local-dev-%s
`, dataplaneID))
	}

	return base64.StdEncoding.EncodeToString(rawKubeconfig)
}
