package serverruntime

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

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

const PermissionStateManagedLifecycleEffectClass contractcp.ComponentManagedEffectClass = "permission_state_advance_v1"

type permissionStateTransactionEffect interface {
	Apply(context.Context, *sql.Tx) (permissionstatev1.AdvanceReceipt, error)
	Prove(context.Context, *sql.DB, permissionstatev1.AdvanceReceipt) (bool, error)
}

func newPermissionStateManagedLifecycleEffectAdapter() packs.ManagedLifecycleEffectAdapter {
	return permissionStateManagedLifecycleEffectAdapter(persistence.NewPermissionStateTransactionEffect())
}

func permissionStateManagedLifecycleEffectAdapter(
	effect permissionStateTransactionEffect,
) packs.ManagedLifecycleEffectAdapter {
	return packs.ManagedLifecycleEffectAdapter{
		Class:      PermissionStateManagedLifecycleEffectClass,
		LockPolicy: packs.ManagedLifecycleEffectManagedStateOnly,
		Apply: func(ctx context.Context, tx *sql.Tx, request packs.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
		},
		Prove: func(ctx context.Context, db *sql.DB, request packs.ManagedLifecycleEffectRequest, encoded []byte) (bool, error) {
			if err := validatePermissionStateManagedLifecycleEffectRequest(request); err != nil {
				return false, err
			}
			receipt, err := permissionstatev1.ParseAdvanceReceipt(encoded)
			if err != nil {
				return false, fmt.Errorf("permission state managed lifecycle effect: parse receipt: %w", err)
			}
			return effect.Prove(ctx, db, receipt)
		},
	}
}

func validatePermissionStateManagedLifecycleEffectRequest(
	request packs.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")
	}
}
