package centralmod

import (
	"context"
	"time"

	"github.com/pingcap/log"
	"github.com/tidbcloud/account/idl/pbgen/proto/account"
	"github.com/tidbcloud/account/idl/pbgen/proto/acl"
	"github.com/tidbcloud/metrics-server/central/db/entity"
	"github.com/tidbcloud/metrics-server/central/db/tenant"
	"github.com/tidbcloud/metrics-server/central/model/account/grpcli"
	"go.uber.org/zap"
	"google.golang.org/protobuf/types/known/timestamppb"
)

func FromTimestamp(t interface{}) *time.Time {
	var tm time.Time
	switch v := t.(type) {
	case timestamppb.Timestamp:
		tm = v.AsTime()
	case *timestamppb.Timestamp:
		if v == nil {
			return nil
		}
		tm = v.AsTime()
	default:
		return nil
	}

	return &tm
}

func convertUser(user *account.User) *entity.User {
	return &entity.User{
		Updatable: entity.Updatable{
			Base: entity.Base{
				ID: entity.ID(user.Id),
			},
		},
		FirstName:   user.FirstName,
		LastName:    user.LastName,
		Email:       user.Email,
		Status:      entity.UserStatus(user.Status),
		InvitedAt:   FromTimestamp(user.InvitedAt),
		Job:         user.Job,
		PhoneNumber: user.PhoneNumber,
	}
}

func convertOrg(org *account.Org) *entity.Tenant {
	return &entity.Tenant{

		Updatable: entity.Updatable{
			Base: entity.Base{
				ID: entity.ID(org.Id),
			},
		},
		Name:                      org.Name,
		Alias:                     org.Alias,
		Email:                     org.Email,
		Country:                   org.Country,
		TimeZone:                  org.TimeZone,
		Status:                    entity.TenantStatus(org.Status),
		Plan:                      entity.TenantPlan(org.Plan),
		POCBindedPackageID:        org.PocBindedPackageId,
		OnDemandBindedPackageID:   org.OnDemandBindedPackageId,
		DevTierBindedPackageID:    org.DevTierBindedPackageId,
		Kind:                      entity.TenantKind(org.Kind),
		AcceptService:             org.AcceptService,
		ZendeskID:                 entity.ID(org.ZendeskId),
		ThirdPartyAccountProvider: &org.ThirdPartyAccountProvider,
		ThirdPartyAccountCode:     &org.ThirdPartyAccountCode,
		Quotas:                    []*entity.Quota{},   // WARN: Empty value, do not use
		Projects:                  []*entity.Project{}, // WARN: Empty value, do not use
		BillingEmail:              org.BillingEmail,
		AccountManager:            org.AccountManager,
	}
}

func convertProject(project *account.Project) *entity.Project {
	return &entity.Project{
		Updatable:   entity.Updatable{Base: entity.Base{ID: entity.ID(project.Id)}},
		Name:        project.Name,
		TenantID:    entity.ID(project.TenantId),
		Tenant:      &entity.Tenant{}, // WARN: Empty value, do not use
		Users:       []entity.User{},  // WARN: Empty value, do not use
		Status:      entity.ProjectStatus(project.Status),
		DeletedTime: FromTimestamp(project.DeletedAt),
	}
}

func GetUser(email string, orgID uint64, skipCheckOrg bool) (*tenant.User, bool) {
	req := &account.GetAuthUserRequest{
		UserEmail:    email,
		OrgId:        orgID,
		SkipCheckOrg: skipCheckOrg,
	}
	authUser, err := grpcli.GetAuthUser(context.Background(), req)
	if err != nil {
		log.Error("Get auth user failed", zap.String("Email", email), zap.Uint64("OrgID", orgID), zap.Error(err))
		return nil, false
	}

	user := convertUser(authUser.User)
	user.Role = convertRole(authUser.OrgRole)
	user.Tenant = convertOrg(authUser.Org)
	user.TenantID = user.Tenant.ID
	for _, project := range authUser.AvailableProjects {
		user.Projects = append(user.Projects, *convertProject(project))
	}

	return &tenant.User{
		User:           *user,
		TenantID:       user.Tenant.ID,
		IsImpersonated: authUser.IsImpersonated,
	}, true
}

func convertRole(rpcRole acl.OrgRole) entity.UserRole {
	if rpcRole == acl.OrgRole_ORG_ROLE_OWNER {
		return entity.UserRoleOwner
	} else if rpcRole == acl.OrgRole_ORG_ROLE_AUDIT_ADMIN {
		return entity.UserRoleAuditAdmin
	} else if rpcRole == acl.OrgRole_ORG_ROLE_BILLING_ADMIN {
		return entity.UserRoleBillingAdmin
	} else if rpcRole == acl.OrgRole_ORG_ROLE_MEMBER {
		return entity.UserRoleMember
	}

	return ""
}

func GetProjectByID(ctx context.Context, projectID uint64) (entity.Project, error) {
	req := &account.GetProjectByIDRequest{
		Id: projectID,
	}
	resp, err := grpcli.GetProjectByID(ctx, req)
	if err != nil {
		log.Error("Get project by id failed", zap.Uint64("projectID", projectID), zap.Error(err))
		return entity.Project{}, err
	}

	project := *convertProject(resp.Project)

	return project, nil
}
