package runtimeapi

import (
	"context"
	"crypto/sha256"
	"encoding/binary"
	"encoding/hex"
	"errors"
	"hash"

	"github.com/nexuscrm/permissions"
	permissionauthoringsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/permissionauthoringv1"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
	"github.com/pingcap-inc/nexuscrm-contract/wire-go/mapping"
)

// PermissionAuthoringEngine is the one transport-neutral aggregate surface.
// Production composition constructs it from the Records/Permissions mutation
// graph; callers cannot inject target resolution, authorization, receipts, or
// child-family writers through the wire request.
type PermissionAuthoringEngine interface {
	ReadPermissionAuthoringSnapshot(
		context.Context,
		permissions.PermissionAuthoringInvocation,
		permissionauthoringsemantic.SnapshotRequest,
	) (permissionauthoringsemantic.SnapshotResponse, error)
	SavePermissionAuthoring(
		context.Context,
		permissions.PermissionAuthoringInvocation,
		permissionauthoringsemantic.Request,
	) (permissionauthoringsemantic.Response, error)
	GetPermissionAuthoringStatus(
		context.Context,
		permissions.PermissionAuthoringInvocation,
		permissionauthoringsemantic.StatusRequest,
	) (permissionauthoringsemantic.StatusResponse, error)
}

type productionPermissionAuthoringEngine struct {
	snapshot *permissions.PermissionAuthoringSnapshotService
	save     *permissions.PermissionAuthoringService
}

func newProductionPermissionAuthoringEngine(
	snapshot *permissions.PermissionAuthoringSnapshotService,
	save *permissions.PermissionAuthoringService,
) (PermissionAuthoringEngine, error) {
	if snapshot == nil || save == nil {
		return nil, errors.New("runtime-api permission authoring aggregate is incomplete")
	}
	return &productionPermissionAuthoringEngine{snapshot: snapshot, save: save}, nil
}

func (engine *productionPermissionAuthoringEngine) ReadPermissionAuthoringSnapshot(
	ctx context.Context,
	invocation permissions.PermissionAuthoringInvocation,
	request permissionauthoringsemantic.SnapshotRequest,
) (permissionauthoringsemantic.SnapshotResponse, error) {
	return engine.snapshot.Read(ctx, invocation, request)
}

func (engine *productionPermissionAuthoringEngine) SavePermissionAuthoring(
	ctx context.Context,
	invocation permissions.PermissionAuthoringInvocation,
	request permissionauthoringsemantic.Request,
) (permissionauthoringsemantic.Response, error) {
	return engine.save.Save(ctx, invocation, request)
}

func (engine *productionPermissionAuthoringEngine) GetPermissionAuthoringStatus(
	ctx context.Context,
	invocation permissions.PermissionAuthoringInvocation,
	request permissionauthoringsemantic.StatusRequest,
) (permissionauthoringsemantic.StatusResponse, error) {
	return engine.save.Status(ctx, invocation, request)
}

type PermissionAuthoringServer struct {
	runtimev1.UnimplementedPermissionAuthoringServiceServer
	engine PermissionAuthoringEngine
}

func NewPermissionAuthoringServer(engine PermissionAuthoringEngine) (*PermissionAuthoringServer, error) {
	if engine == nil {
		return nil, errors.New("runtime-api permission authoring engine is required")
	}
	return &PermissionAuthoringServer{engine: engine}, nil
}

func (server *PermissionAuthoringServer) ReadPermissionAuthoringSnapshot(
	ctx context.Context,
	wireRequest *runtimev1.ReadPermissionAuthoringSnapshotRequest,
) (*runtimev1.ReadPermissionAuthoringSnapshotResponse, error) {
	request, err := mapping.FromWireReadPermissionAuthoringSnapshotRequest(ctx, wireRequest)
	if err != nil {
		return nil, permissionAuthoringStatus(err)
	}
	invocation, err := permissionAuthoringInvocationFromContext(ctx)
	if err != nil {
		return nil, permissionAuthoringStatus(err)
	}
	response, err := server.engine.ReadPermissionAuthoringSnapshot(ctx, invocation, request)
	if err != nil {
		return nil, permissionAuthoringStatus(err)
	}
	wireResponse, err := mapping.ToWireReadPermissionAuthoringSnapshotResponse(request, response)
	if err != nil {
		return nil, permissionAuthoringStatus(err)
	}
	return wireResponse, nil
}

func (server *PermissionAuthoringServer) SavePermissionAuthoring(
	ctx context.Context,
	wireRequest *runtimev1.SavePermissionAuthoringRequest,
) (*runtimev1.SavePermissionAuthoringResponse, error) {
	request, err := mapping.FromWireSavePermissionAuthoringRequest(ctx, wireRequest)
	if err != nil {
		return nil, permissionAuthoringStatus(err)
	}
	invocation, err := permissionAuthoringInvocationFromContext(ctx)
	if err != nil {
		return nil, permissionAuthoringStatus(err)
	}
	response, err := server.engine.SavePermissionAuthoring(ctx, invocation, request)
	if err != nil {
		return nil, permissionAuthoringStatus(err)
	}
	wireResponse, err := mapping.ToWireSavePermissionAuthoringResponse(request, response)
	if err != nil {
		return nil, permissionAuthoringStatus(err)
	}
	return wireResponse, nil
}

func (server *PermissionAuthoringServer) GetPermissionAuthoringStatus(
	ctx context.Context,
	wireRequest *runtimev1.GetPermissionAuthoringStatusRequest,
) (*runtimev1.GetPermissionAuthoringStatusResponse, error) {
	request, err := mapping.FromWireGetPermissionAuthoringStatusRequest(ctx, wireRequest)
	if err != nil {
		return nil, permissionAuthoringStatus(err)
	}
	invocation, err := permissionAuthoringInvocationFromContext(ctx)
	if err != nil {
		return nil, permissionAuthoringStatus(err)
	}
	response, err := server.engine.GetPermissionAuthoringStatus(ctx, invocation, request)
	if err != nil {
		return nil, permissionAuthoringStatus(err)
	}
	wireResponse, err := mapping.ToWireGetPermissionAuthoringStatusResponse(request, response)
	if err != nil {
		return nil, permissionAuthoringStatus(err)
	}
	return wireResponse, nil
}

// permissionAuthoringInvocationFromContext derives every authority fact from
// the two admission-owned context values. The semantic request contains no
// org, principal, AppInstallation, Permission State, or receipt override.
func permissionAuthoringInvocationFromContext(ctx context.Context) (permissions.PermissionAuthoringInvocation, error) {
	trusted, trustedOK := trustedCallFromContext(ctx)
	scope, scopeOK := runtimeExecutionScopeFromContext(ctx)
	if !trustedOK || !scopeOK || scope.capability != permissionauthoringsemantic.CapabilityName ||
		scope.orgID == "" || scope.appInstallationID == "" ||
		scope.orgID != trusted.orgID || scope.appInstallationID != trusted.appInstallationID {
		return permissions.PermissionAuthoringInvocation{}, &permissionauthoringsemantic.Failure{
			Category: permissionauthoringsemantic.FailureUnavailable,
		}
	}
	actor := trusted.UserSession()
	return permissions.PermissionAuthoringInvocation{
		AuthorityScope:               permissionAuthoringAuthorityScope(scope.orgID, scope.appInstallationID),
		Actor:                        &actor,
		AdmittedPermissionStateFloor: trusted.PermissionFloor(),
	}, nil
}

func permissionAuthoringAuthorityScope(orgID, appInstallationID string) string {
	digest := sha256.New()
	_, _ = digest.Write([]byte("nexuscrm.permission-authoring-authority-scope.v1"))
	permissionAuthoringHashFrame(digest, orgID)
	permissionAuthoringHashFrame(digest, appInstallationID)
	return hex.EncodeToString(digest.Sum(nil))
}

func permissionAuthoringHashFrame(digest hash.Hash, value string) {
	var size [8]byte
	binary.BigEndian.PutUint64(size[:], uint64(len(value)))
	_, _ = digest.Write(size[:])
	_, _ = digest.Write([]byte(value))
}

type runtimePermissionAuthoringReceiptSource struct{}

func (runtimePermissionAuthoringReceiptSource) PermissionAuthoringReceipts(
	ctx context.Context,
) (permissionauthoringsemantic.Receipts, error) {
	scope, scopeOK := runtimeExecutionScopeFromContext(ctx)
	trusted, trustedOK := trustedCallFromContext(ctx)
	if !scopeOK || !trustedOK || scope.capability != permissionauthoringsemantic.CapabilityName ||
		scope.orgID != trusted.orgID || scope.appInstallationID != trusted.appInstallationID {
		return permissionauthoringsemantic.Receipts{}, &permissionauthoringsemantic.Failure{
			Category: permissionauthoringsemantic.FailureUnavailable,
		}
	}
	return receiptsForCapability(scope.receipts, permissionauthoringsemantic.CapabilityName), nil
}

func permissionAuthoringStatus(err error) error {
	return mapping.ToPlatformStatusError(err, permissionauthoringsemantic.CapabilityName)
}
