package service

import (
	"context"
	"fmt"
	"sync"

	coreapi "github.com/tidbcloud/core-svc/idl/pbgen/proto/core"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.buf.build/protocolbuffers/go/linuxgit/tidbcloud/base"
	"go.uber.org/zap"
)

type MockCoreSvc struct {
	coreapi.UnimplementedCoreServiceServer

	name string
	// Map: TenantId - ProjectId - ClusterId - Cluster
	clusters          map[uint64]map[uint64]map[uint64]*coreapi.Cluster
	peerings          []*coreapi.NetworkPeer
	next_cluster_id   uint64
	accessPointRespId uint64
	lock              sync.Mutex
}

func NewMockCoreSvc(name string) *MockCoreSvc {
	return &MockCoreSvc{
		name:            name,
		clusters:        make(map[uint64]map[uint64]map[uint64]*coreapi.Cluster),
		next_cluster_id: 100000000,
	}
}

func (svc *MockCoreSvc) Reset() {
	svc.lock.Lock()
	defer svc.lock.Unlock()
	svc.clusters = make(map[uint64]map[uint64]map[uint64]*coreapi.Cluster)
	svc.next_cluster_id = 100000000
	svc.accessPointRespId = 100000000
	svc.peerings = []*coreapi.NetworkPeer{}
}

// For testing only
func (svc *MockCoreSvc) AddNetworkPeer(peering *coreapi.NetworkPeer) {
	svc.lock.Lock()
	defer svc.lock.Unlock()
	svc.peerings = append(svc.peerings, peering)
}

// Return a fake cluster id with patterns, so the caller knows the request reached here

func (svc *MockCoreSvc) CreateDBUser(
	ctx context.Context,
	in *coreapi.CreateDBUserReq,
) (*coreapi.CreateDBUserResp, error) {
	tilog.PlainInfo("mock-core-svc",
		zap.String("service", svc.name),
		zap.String("method", "CreateDBUser"),
		zap.Uint64("cluster-id", in.ClusterId))
	return &coreapi.CreateDBUserResp{
		BaseResp: &base.BaseResp{ErrMsg: "AllGood"},
	}, nil
}

func (svc *MockCoreSvc) GetDBUser(ctx context.Context, in *coreapi.GetDBUserReq) (*coreapi.GetDBUserResp, error) {
	tilog.PlainInfo("mock-core-svc",
		zap.String("service", svc.name),
		zap.String("method", "GetDBUser"),
		zap.Uint64("cluster-id", in.ClusterId))
	return &coreapi.GetDBUserResp{
		User: &coreapi.DBUser{
			Username: "fake-user-name-in-mock-core-svc",
		},
	}, nil
}

func (svc *MockCoreSvc) CreateIamRole(
	ctx context.Context,
	in *coreapi.CreateIamRoleReq,
) (*coreapi.CreateIamRoleResp, error) {
	tilog.PlainInfo("mock-core-svc",
		zap.String("service", svc.name),
		zap.String("method", "CreateIamRole"))
	return &coreapi.CreateIamRoleResp{}, nil
}

func (svc *MockCoreSvc) ListIamRoles(
	ctx context.Context,
	in *coreapi.ListIamRolesReq,
) (*coreapi.ListIamRolesResp, error) {
	tilog.PlainInfo("mock-core-svc",
		zap.String("service", svc.name),
		zap.String("method", "ListIamRoles"))
	return &coreapi.ListIamRolesResp{}, nil
}

func (svc *MockCoreSvc) GetPrivateLinkService(
	ctx context.Context,
	in *coreapi.GetPrivateLinkServiceReq,
) (*coreapi.GetPrivateLinkServiceResp, error) {
	tilog.PlainInfo("mock-core-svc",
		zap.String("service", svc.name),
		zap.String("method", "GetPrivateLinkService"))
	return &coreapi.GetPrivateLinkServiceResp{}, nil
}

func (svc *MockCoreSvc) CreatePrivateLinkEndpoint(
	ctx context.Context,
	in *coreapi.CreatePrivateLinkEndpointReq,
) (*coreapi.CreatePrivateLinkEndpointResp, error) {
	tilog.PlainInfo("mock-core-svc",
		zap.String("service", svc.name),
		zap.String("method", "CreatePrivateLinkEndpoint"))
	return &coreapi.CreatePrivateLinkEndpointResp{
		PrivatelinkEndpoint: &coreapi.PrivateLinkEndpoint{
			ClusterId: in.ClusterId,
		},
	}, nil
}

func (svc *MockCoreSvc) ListPrivateLinkEndpoints(
	ctx context.Context,
	in *coreapi.ListPrivateLinkEndpointsReq,
) (*coreapi.ListPrivateLinkEndpointsResp, error) {
	tilog.PlainInfo("mock-core-svc",
		zap.String("service", svc.name),
		zap.String("method", "ListPrivateLinkEndpoints"))
	var endpoints []*coreapi.PrivateLinkEndpoint
	svc.lock.Lock()
	defer svc.lock.Unlock()
	for tenant, projects := range svc.clusters {
		if in.OrgId != tenant {
			continue
		}
		for project, clusters := range projects {
			if in.ProjectId != project {
				continue
			}
			for clusterId := range clusters {
				endpoint := &coreapi.PrivateLinkEndpoint{ClusterId: clusterId}
				endpoints = append(endpoints, endpoint)
			}
		}
	}
	return &coreapi.ListPrivateLinkEndpointsResp{
		Endpoints: endpoints,
	}, nil
}

func (svc *MockCoreSvc) ChangeClusterPasswd(
	ctx context.Context,
	in *coreapi.ChangeClusterPasswdReq,
) (*coreapi.ChangeClusterPasswdResp, error) {
	tilog.PlainInfo("mock-core-svc",
		zap.String("service", svc.name),
		zap.String("method", "ChangeClusterPasswd"))
	return &coreapi.ChangeClusterPasswdResp{}, nil
}

func (svc *MockCoreSvc) ListRegionCIDROfProject(
	ctx context.Context,
	in *coreapi.ListRegionCIDROfProjectReq,
) (*coreapi.ListRegionCIDROfProjectResp, error) {
	tilog.PlainInfo("mock-core-svc",
		zap.String("service", svc.name),
		zap.String("method", "ListRegionCIDROfProject"))
	return &coreapi.ListRegionCIDROfProjectResp{}, nil
}

func (svc *MockCoreSvc) ListCloudRegions(
	ctx context.Context,
	in *coreapi.ListCloudRegionsReq,
) (*coreapi.ListCloudRegionsResp, error) {
	tilog.PlainInfo("mock-core-svc",
		zap.String("service", svc.name),
		zap.String("method", "ListCloudRegions"))
	return &coreapi.ListCloudRegionsResp{}, nil
}

func (svc *MockCoreSvc) ListNetworkPeer(
	ctx context.Context,
	in *coreapi.ListNetworkPeerReq,
) (*coreapi.ListNetworkPeerResp, error) {
	resp := &coreapi.ListNetworkPeerResp{}
	svc.lock.Lock()
	defer svc.lock.Unlock()
	resp.PeeringList = append(resp.PeeringList, svc.peerings...)
	tilog.PlainInfo("mock-core-svc",
		zap.String("service", svc.name),
		zap.String("method", "ListNetworkPeer"),
		zap.String("resp", fmt.Sprintf("%v", resp.PeeringList)))
	return resp, nil
}
