package usecase

import (
	"context"
	"strings"

	"gateway/internal/model/permission/domain"
	"gateway/internal/model/permission/domain/entity"
	"gateway/internal/model/permission/domain/enum"
	domrepo "gateway/internal/model/permission/domain/repository"
	dom "gateway/internal/model/permission/domain/usecase"
)

// RoleMappingUseCaseParam injects mapping + role repositories.
type RoleMappingUseCaseParam struct {
	Roles    domrepo.RoleRepository
	Mappings domrepo.RoleMappingRepository
}

type roleMappingUseCase struct {
	roles    domrepo.RoleRepository
	mappings domrepo.RoleMappingRepository
}

// NewRoleMappingUseCase returns the external→internal mapping editor.
func NewRoleMappingUseCase(param RoleMappingUseCaseParam) dom.RoleMappingUseCase {
	return &roleMappingUseCase{
		roles:    param.Roles,
		mappings: param.Mappings,
	}
}

func (uc *roleMappingUseCase) Upsert(
	ctx context.Context,
	param *dom.UpsertMappingParam,
) (*entity.RoleMapping, error) {
	if param == nil ||
		param.TenantID == "" ||
		param.ExternalKey == "" ||
		param.InternalRoleKey == "" {
		return nil, errb.InputMissingRequired("tenant_id|external_key|internal_role_key")
	}
	if !param.ExternalSource.IsValid() {
		return nil, errb.InputInvalidFormat("invalid external_source")
	}
	if param.ExternalSource == enum.RoleSourceManual {
		return nil, errb.InputInvalidFormat("manual source not allowed for mapping")
	}
	role, err := uc.roles.GetByKey(ctx, param.TenantID, param.InternalRoleKey)
	if err != nil {
		return nil, wrapRepoErr(err)
	}
	rm := &entity.RoleMapping{
		TenantID:        param.TenantID,
		ExternalSource:  param.ExternalSource,
		ExternalKey:     strings.TrimSpace(param.ExternalKey),
		InternalRoleID:  role.ID.Hex(),
		InternalRoleKey: role.Key,
	}
	if err := uc.mappings.Upsert(ctx, rm); err != nil {
		return nil, wrapRepoErr(err, "upsert mapping")
	}
	return rm, nil
}

func (uc *roleMappingUseCase) Delete(
	ctx context.Context,
	tenantID string,
	source enum.RoleSource,
	externalKey string,
) error {
	if !source.IsValid() {
		return errb.InputInvalidFormat("invalid external_source")
	}
	if err := uc.mappings.Delete(ctx, tenantID, source, externalKey); err != nil {
		return wrapRepoErr(err, "delete mapping")
	}
	return nil
}

func (uc *roleMappingUseCase) GetByExternal(
	ctx context.Context,
	tenantID string,
	source enum.RoleSource,
	externalKey string,
) (*entity.RoleMapping, error) {
	rm, err := uc.mappings.GetByExternal(ctx, tenantID, source, externalKey)
	if err != nil {
		return nil, wrapRepoErr(err)
	}
	return rm, nil
}

func (uc *roleMappingUseCase) List(
	ctx context.Context,
	tenantID string,
	query *dom.ListMappingQuery,
) ([]*entity.RoleMapping, int64, error) {
	var source *enum.RoleSource
	var offset int64
	limit := int64(domain.RoleMappingPageSize)
	if query != nil {
		if query.Source != nil {
			source = query.Source
		}
		if query.Offset > 0 {
			offset = query.Offset
		}
		if query.Limit > 0 {
			limit = query.Limit
		}
	}
	docs, total, err := uc.mappings.ListByTenant(ctx, tenantID, source, offset, limit)
	if err != nil {
		return nil, 0, wrapRepoErr(err)
	}
	return docs, total, nil
}

var _ dom.RoleMappingUseCase = (*roleMappingUseCase)(nil)
