package service

import (
	"context"

	"github.com/pingcap/log"
	"github.com/tidbcloud/dataflow-service/internal/bizutils"
	"github.com/tidbcloud/dataflow-service/internal/db/entity"
	"github.com/tidbcloud/dataflow-service/pkg/account/accountdb"
	"github.com/tidbcloud/dataflow-service/pkg/account/accounttypes"
	"github.com/tidbcloud/pkgv2/bizerr"
	pkglog "github.com/tidbcloud/pkgv2/log"
	"go.uber.org/zap"
)

type UserContext struct {
	user *accountdb.User
	log  *zap.Logger
}

func NewUserContext(ctx context.Context) *UserContext {
	user, err := bizutils.GetUserFromContext(ctx)
	if err != nil {
		panic("expected user claim")
	}

	logger := log.L().With(zap.Uint64("tenantID", user.TenantID),
		zap.String("publicKey", user.LastName),
		zap.String("module", "backup")).
		With(pkglog.FieldsFromContext(ctx)...)

	return &UserContext{
		user: user,
		log:  logger,
	}
}

func (u *UserContext) User() *accountdb.User {
	return u.user
}

func (u *UserContext) OrgID() entity.ID {
	return entity.ID(u.user.TenantID)
}

func (u *UserContext) UserEmail() string {
	return u.user.Email
}

func (u *UserContext) UserID() entity.ID {
	return entity.ID(u.user.User.ID)
}

func (u *UserContext) Logger() *zap.Logger {
	return u.log
}

func (u *UserContext) CheckOrgIDAndProjectID(orgID, projectID entity.ID) bizerr.BizError {
	err := u.CheckOrgID(orgID)
	if err != nil {
		return err
	}
	return u.CheckProjectID(projectID)
}

func (u *UserContext) CheckOrgID(orgID entity.ID) bizerr.BizError {
	user := u.user
	if user.IsImpersonated {
		return nil
	}

	if orgID.Uint64() != user.TenantID {
		return bizerr.New(bizerr.ErrCodePermission, "org_id %d is inconsistent with the user's org ID", orgID)
	}
	return nil
}

func (u *UserContext) userInProject(userProjects []accounttypes.Project, projectID entity.ID) bool {
	for _, project := range userProjects {
		if project.ID == projectID.Uint64() {
			return true
		}
	}
	return false
}

func (u *UserContext) CheckProjectID(projectID entity.ID) bizerr.BizError {
	user := u.user
	if u.userInProject(user.Projects, projectID) {
		return nil
	}
	return bizerr.New(bizerr.ErrCodePermission, "you do not have permission to access this project, project_id: %d", projectID)
}
