package pkg

import (
	"context"
	"fmt"
	"time"

	"github.com/gogo/protobuf/types"
	"github.com/pingcap/errcode"
	"github.com/pingcap/log"
	"github.com/pkg/errors"
	"github.com/tidbcloud/debug_portal/grpc/grpcli"
	"github.com/tidbcloud/debug_portal/idl/pbgen/base"
	"github.com/tidbcloud/debug_portal/idl/pbgen/core"
	"github.com/tidbcloud/debug_portal/idl/pbgen/debug_portal"
	"github.com/tidbcloud/debug_portal/model/admin"
	"go.uber.org/zap"
	"k8s.io/utils/pointer"
)

func (service *debugPortalService) ListTenants(context context.Context, req *debug_portal.ListTenantsReq) (*debug_portal.ListTenantsResp, error) {
	if err := service.authN(context); err != nil {
		return nil, err
	}
	// FIXME: Currently this is completely copied from admin model.
	items, total, err := service.model.ListTenants(req.Name, req.Id, req.Email, req.Status, req.Page, req.PerPage)
	if err != nil {
		return nil, err
	}
	var results = &debug_portal.ListTenantsResp{
		Total: uint32(total),
		Items: []*debug_portal.Tenant{},
	}
	for _, item := range items {
		t := buildAPITenantItem(item)
		results.Items = append(results.Items, t)
	}
	return results, nil
}

func buildAPITenantItem(item *admin.Tenant) *debug_portal.Tenant {
	// FIXME: Currently this is completely copied from admin model.
	t := &debug_portal.Tenant{
		Id:                  item.ID.Uint64(),
		Name:                item.Name,
		Email:               item.Email,
		Timezone:            item.Timezone,
		Status:              item.Status,
		Plan:                item.Plan,
		DiscountDescription: item.DiscountDescription,
		Discount:            item.Discount,
		Country:             item.Country,
		HasDiscount:         item.HasDiscount,
	}
	return t
}

func (service *debugPortalService) GetTenantById(context context.Context, req *debug_portal.OrgIdReq) (*debug_portal.Tenant, error) {
	if err := service.authN(context); err != nil {
		return nil, err
	}
	// FIXME: Currently this is completely copied from admin model.
	items, _, err := service.model.ListTenants("", req.OrgId, "", nil, 0, 0)
	if err != nil {
		return nil, err
	}
	if len(items) > 0 {
		return buildAPITenantItem(items[0]), nil
	}
	return nil, errcode.NewNotFoundErr(fmt.Errorf("organization <%d> is not found", req.OrgId))
}

func (service *debugPortalService) ListTenantClusters(ctx context.Context, req *debug_portal.ListTenantClustersReq) (*debug_portal.ListTenantClustersResp, error) {
	if err := service.authN(ctx); err != nil {
		return nil, err
	}
	filter := &core.ClusterFilter{
		TenantIds: []uint64{req.OrgId},
		Deleted:   pointer.BoolPtr(false),
	}
	pager := &base.Pager{
		Page:     int64(req.Page),
		PageSize: int64(req.PerPage),
	}
	sorter := &base.Sorter{
		OrderBy: "created_at",
		Sort:    base.Sort_SORT_DESC,
	}

	listReq := &core.ListClustersReq{
		Filter:  filter,
		Pager:   pager,
		Sorter:  sorter,
		BaseReq: nil,
	}
	listResp, err := grpcli.ListClusters(ctx, listReq)
	if err != nil {
		return nil, err
	}
	countResp, err := grpcli.CountClusters(ctx, &core.CountClustersReq{Filter: filter})
	if err != nil {
		return nil, err
	}

	outs := make([]*debug_portal.TenantCluster, 0, len(listResp.Clusters))
	for _, clusterId := range listResp.OrderedClusterIds {
		cluster := listResp.Clusters[clusterId]
		log.Info("Find the cluster.", zap.Uint64("cluster_id", cluster.Id),
			zap.String("status", cluster.Status.String()),
			zap.String("user_status", cluster.UserStatus.String()),
			zap.String("cluster_name", cluster.Name),
		)
		createdAt, _ := types.TimestampProto(time.Unix(cluster.CreatedAt, 0))

		out := &debug_portal.TenantCluster{
			Id:               int64(cluster.Id),
			Name:             cluster.Name,
			Provider:         cluster.CloudProviderName,
			Region:           cluster.RegionName,
			Profile:          "", // not used
			Version:          cluster.Version,
			CreateTime:       createdAt,
			TenantComponents: &debug_portal.TenantComponents{},
			Status:           debug_portal.Cluster_Status(cluster.Status),

			CrossAccountDeploymentId: cluster.XaccountDeployId,
		}
		for _, component := range cluster.Components {
			switch component.ComponentType {
			case core.ComponentType_COMPONENT_TYPE_TIDB:
				out.TenantComponents.Tidb = &debug_portal.TenantComponent{
					Replicas:    uint32(component.Replicas),
					MachineType: component.ComputeInstanceType,
				}
			case core.ComponentType_COMPONENT_TYPE_PD:
				out.TenantComponents.Pd = &debug_portal.TenantComponent{
					Replicas:    uint32(component.Replicas),
					MachineType: component.ComputeInstanceType,
				}
			case core.ComponentType_COMPONENT_TYPE_TIFLASH:
				out.TenantComponents.Tiflash = &debug_portal.TenantComponent{
					Replicas:    uint32(component.Replicas),
					MachineType: component.ComputeInstanceType,
				}
			case core.ComponentType_COMPONENT_TYPE_TIKV:
				out.TenantComponents.Tikv = &debug_portal.TenantComponent{
					Replicas:    uint32(component.Replicas),
					MachineType: component.ComputeInstanceType,
				}
			default:
				return nil, errors.Errorf("unknown component %s", string(component.ComponentType))
			}
		}
		outs = append(outs, out)
	}
	return &debug_portal.ListTenantClustersResp{
		Total: uint32(countResp.Count),
		Items: outs,
	}, nil
}

func (service *debugPortalService) GetDashboardLinks(context context.Context, req *debug_portal.GetDashboardLinksReq) (*debug_portal.GetDashboardLinksResp, error) {
	if err := service.authN(context); err != nil {
		return nil, err
	}
	// FIXME: Currently this is completely copied from admin model.
	ds, err := service.model.GetDashboardLinks(req.OrgId, req.ClusterId)
	if err != nil {
		return nil, err
	}
	var items []*debug_portal.DashboardLink
	for _, item := range ds {
		items = append(items, &debug_portal.DashboardLink{Name: item.Name, Link: item.Link})
	}
	return &debug_portal.GetDashboardLinksResp{Dashboards: items}, nil
}
