package permissionsimp

import (
	"context"

	api "github.com/tech/sendico/pkg/api/http"
	"github.com/tech/sendico/pkg/auth"
	"github.com/tech/sendico/pkg/db/organization"
	"github.com/tech/sendico/pkg/db/policy"
	"github.com/tech/sendico/pkg/db/role"
	"github.com/tech/sendico/pkg/db/transaction"
	"github.com/tech/sendico/pkg/mlogger"
	"github.com/tech/sendico/pkg/model"
	"github.com/tech/sendico/pkg/mservice"
	eapi "github.com/tech/sendico/server/interface/api"
	mutil "github.com/tech/sendico/server/internal/mutil/param"
	"go.mongodb.org/mongo-driver/v2/bson"
	"go.uber.org/zap"
)

type PermissionsAPI struct {
	logger                mlogger.Logger
	db                    organization.DB
	pdb                   policy.DB
	rdb                   role.DB
	enforcer              auth.Enforcer
	manager               auth.Manager
	rolesPermissionRef    bson.ObjectID
	policiesPermissionRef bson.ObjectID
	Rph                   mutil.ParamHelper
	tf                    transaction.Factory
	auth                  auth.Manager
}

func (a *PermissionsAPI) Name() mservice.Type {
	return mservice.Permissions
}

func (a *PermissionsAPI) Finish(_ context.Context) error {
	return nil
}

func CreateAPI(a eapi.API) (*PermissionsAPI, error) {
	p := &PermissionsAPI{
		enforcer: a.Permissions().Enforcer(),
		manager:  a.Permissions().Manager(),
		Rph:      mutil.CreatePH("role"),
		tf:       a.DBFactory().TransactionFactory(),
		auth:     a.Permissions().Manager(),
	}
	p.logger = a.Logger().Named(p.Name())

	var err error
	if p.db, err = a.DBFactory().NewOrganizationDB(); err != nil {
		p.logger.Error("Failed to create organizations database", zap.Error(err))
		return nil, err
	}
	if p.rdb, err = a.DBFactory().NewRolesDB(); err != nil {
		p.logger.Error("Failed to create roles database", zap.Error(err))
		return nil, err
	}
	if p.pdb, err = a.DBFactory().NewPoliciesDB(); err != nil {
		p.logger.Error("Failed to create policies database", zap.Error(err))
		return nil, err
	}

	var pdesc model.PolicyDescription
	if err := p.pdb.GetBuiltInPolicy(context.Background(), mservice.Roles, &pdesc); err != nil {
		p.logger.Warn("Failed to fetch roles management permission description", zap.Error(err))
		return nil, err
	}
	p.rolesPermissionRef = pdesc.ID
	if err := p.pdb.GetBuiltInPolicy(context.Background(), mservice.Policies, &pdesc); err != nil {
		p.logger.Warn("Failed to fetch policies management permission description", zap.Error(err))
		return nil, err
	}
	p.policiesPermissionRef = pdesc.ID

	a.Register().AccountHandler(p.Name(), mutil.AddOrganizaztionRef("/"), api.Get, p.get)
	a.Register().AccountHandler(p.Name(), mutil.AddOrganizaztionRef("/all"), api.Get, p.getAll)
	a.Register().AccountHandler(p.Name(), mutil.AddOrganizaztionRef("/change_role"), api.Post, p.changeRole)
	a.Register().AccountHandler(p.Name(), "/policies", api.Put, p.changePolicies)
	a.Register().AccountHandler(p.Name(), "/role", api.Post, p.createRoleDescription)
	a.Register().AccountHandler(p.Name(), p.Rph.AddRef("/role"), api.Delete, p.deleteRoleDescription)

	return p, nil
}
