package managedruntime

import (
	"context"
	"database/sql"
	"errors"
	"fmt"

	"github.com/nexuscrm/persistence"
	"github.com/nexuscrm/persistence/managedlifecycle"
	contractcp "github.com/pingcap-inc/nexuscrm-contract/pkg/controlplane"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/permissionstatev1"
)

const PermissionStateManagedLifecycleEffectClass contractcp.ComponentManagedEffectClass = "permission_state_advance_v1"

type PermissionStateTransactionEffect interface {
	Apply(context.Context, *sql.Tx) (permissionstatev1.AdvanceReceipt, error)
}

func NewPermissionStateManagedLifecycleEffectAdapter() managedlifecycle.ManagedLifecycleEffectAdapter {
	return PermissionStateManagedLifecycleEffectAdapter(persistence.NewPermissionStateTransactionEffect())
}

func PermissionStateManagedLifecycleEffectAdapter(effect PermissionStateTransactionEffect) managedlifecycle.ManagedLifecycleEffectAdapter {
	return managedlifecycle.ManagedLifecycleEffectAdapter{
		Class:      PermissionStateManagedLifecycleEffectClass,
		LockPolicy: managedlifecycle.ManagedLifecycleEffectManagedStateOnly,
		Apply: func(ctx context.Context, tx *sql.Tx, request managedlifecycle.ManagedLifecycleEffectRequest) ([]byte, error) {
			if err := ValidatePermissionStateManagedLifecycleEffectRequest(request); err != nil {
				return nil, err
			}
			receipt, err := effect.Apply(ctx, tx)
			if err != nil {
				return nil, err
			}
			encoded, err := receipt.MarshalBinary()
			if err != nil {
				return nil, fmt.Errorf("permission state managed lifecycle effect: encode receipt: %w", err)
			}
			return encoded, nil
		},
	}
}

func ValidatePermissionStateManagedLifecycleEffectRequest(request managedlifecycle.ManagedLifecycleEffectRequest) error {
	if len(request.OperationID) == 0 || request.Attempt < 1 || request.Kind == "" {
		return errors.New("permission state managed lifecycle effect: invalid request")
	}
	switch request.Scope {
	case contractcp.ComponentManagedScopeItem,
		contractcp.ComponentManagedScopeSet,
		contractcp.ComponentManagedScopePlannedTransaction:
	default:
		return errors.New("permission state managed lifecycle effect: invalid request")
	}
	switch request.Disposition {
	case contractcp.ComponentManagedMutationDesiredProjectionStaged,
		contractcp.ComponentManagedMutationSafeProjectionStaged:
		return nil
	default:
		return errors.New("permission state managed lifecycle effect: semantic change disposition is required")
	}
}
