package persistence

import (
	"context"
	"crypto/sha256"
	"database/sql"
	"encoding/hex"
	"errors"
	"strings"
	"unicode"
	"unicode/utf8"

	applicationresultsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/applicationresultv1"
	commonsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
	flowcommandsemanticv3 "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/flowcommandv3"
)

const (
	runtimePlatformCommandTrustedIdentityMaxBytes = 255
	runtimePlatformCommandTrustedWorkloadMaxBytes = 1024
)

// RuntimePlatformCommandTrustedScope is the complete host-authenticated
// acceptance partition. None of these facts comes from the result request.
type RuntimePlatformCommandTrustedScope struct {
	OrgID               string
	AppInstallationID   string
	CallerWorkloadID    string
	RuntimeCapabilityID string
	// PrincipalID is set by end-user-authored typed operations. Machine-owned
	// command families retain their historical workload-scoped partition by
	// leaving it empty.
	PrincipalID string
}

// Digest reproduces the exact acceptance-time ScopeDigest grammar. Embedded
// NULs are rejected so the delimiters cannot create an ambiguous identity.
func (s RuntimePlatformCommandTrustedScope) Digest() (string, error) {
	if !validRuntimePlatformCommandTrustedIdentity(s.OrgID, runtimePlatformCommandTrustedIdentityMaxBytes) ||
		!validRuntimePlatformCommandTrustedIdentity(s.AppInstallationID, runtimePlatformCommandTrustedIdentityMaxBytes) ||
		!validRuntimePlatformCommandTrustedIdentity(s.CallerWorkloadID, runtimePlatformCommandTrustedWorkloadMaxBytes) ||
		!validRuntimePlatformCommandTrustedIdentity(s.RuntimeCapabilityID, runtimePlatformCommandTrustedIdentityMaxBytes) ||
		(s.PrincipalID != "" && !validRuntimePlatformCommandTrustedIdentity(s.PrincipalID, runtimePlatformCommandTrustedIdentityMaxBytes)) {
		return "", &applicationresultsemantic.Failure{Category: commonsemantic.FailureInternal}
	}
	identity :=
		s.OrgID + "\x00" + s.AppInstallationID + "\x00" +
			s.CallerWorkloadID + "\x00" + s.RuntimeCapabilityID
	if s.PrincipalID != "" {
		identity += "\x00" + s.PrincipalID
	}
	digest := sha256.Sum256([]byte(identity))
	return hex.EncodeToString(digest[:]), nil
}

func validRuntimePlatformCommandTrustedIdentity(value string, maximum int) bool {
	return value != "" && len(value) <= maximum && utf8.ValidString(value) &&
		strings.TrimSpace(value) == value && strings.IndexFunc(value, unicode.IsControl) < 0
}

const runtimePlatformApplicationResultQuery = `SELECT CommandId, CommandKind, PayloadFormatVersion, Payload, State, ResultPayload
FROM _System_RuntimePlatformCommand
WHERE ScopeDigest = ? AND CommandId = ?
  AND OrgId = ? AND AppInstallationId = ?
  AND CallerWorkloadId = ? AND RuntimeCapabilityId = ?
LIMIT 1`

// GetApplicationResult reads the committed Runtime-owned result for one exact
// trusted scope and persisted flowcommandv3 execution binding. Older payload
// versions deliberately remain indistinguishable from an unknown command.
func (r *RuntimePlatformCommandRepository) GetApplicationResult(
	ctx context.Context,
	scope RuntimePlatformCommandTrustedScope,
	request applicationresultsemantic.GetResultRequest,
) (applicationresultsemantic.GetResultResponse, error) {
	if err := applicationresultsemantic.ValidateGetResultRequest(ctx, request); err != nil {
		return applicationresultsemantic.GetResultResponse{}, err
	}
	if r == nil || r.db == nil {
		return applicationresultsemantic.GetResultResponse{}, runtimePlatformApplicationResultFailure(commonsemantic.FailureUnavailable)
	}
	scopeDigest, err := scope.Digest()
	if err != nil {
		return applicationresultsemantic.GetResultResponse{}, runtimePlatformApplicationResultFailure(commonsemantic.FailureInternal)
	}

	var persistedCommandID, commandKind, state string
	var payloadFormatVersion uint32
	var payload []byte
	var encodedResult sql.NullString
	err = r.executor(ctx).QueryRowContext(
		ctx,
		runtimePlatformApplicationResultQuery,
		scopeDigest,
		request.CommandID,
		scope.OrgID,
		scope.AppInstallationID,
		scope.CallerWorkloadID,
		scope.RuntimeCapabilityID,
	).Scan(&persistedCommandID, &commandKind, &payloadFormatVersion, &payload, &state, &encodedResult)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return applicationresultsemantic.GetResultResponse{}, runtimePlatformApplicationResultFailure(commonsemantic.FailureMissing)
		}
		return applicationresultsemantic.GetResultResponse{}, runtimePlatformApplicationResultReadFailure(ctx)
	}
	if persistedCommandID != request.CommandID ||
		commandKind != flowcommandsemanticv3.CommandKind ||
		payloadFormatVersion != flowcommandsemanticv3.PayloadFormatVersion {
		return applicationresultsemantic.GetResultResponse{}, runtimePlatformApplicationResultFailure(commonsemantic.FailureMissing)
	}

	command, err := flowcommandsemanticv3.Decode(payload)
	if err != nil {
		return applicationresultsemantic.GetResultResponse{}, runtimePlatformApplicationResultFailure(commonsemantic.FailureInternal)
	}
	executionRefDigest, err := applicationresultsemantic.ExecutionRefDigest(command.ExecutionRef)
	if err != nil {
		return applicationresultsemantic.GetResultResponse{}, runtimePlatformApplicationResultFailure(commonsemantic.FailureInternal)
	}
	if executionRefDigest != request.ExecutionRefDigest {
		return applicationresultsemantic.GetResultResponse{}, runtimePlatformApplicationResultFailure(commonsemantic.FailureMissing)
	}

	switch state {
	case RuntimePlatformCommandAccepted,
		RuntimePlatformCommandProcessing,
		RuntimePlatformCommandBlocked,
		RuntimePlatformCommandPoisoned:
		if encodedResult.Valid {
			return applicationresultsemantic.GetResultResponse{}, runtimePlatformApplicationResultFailure(commonsemantic.FailureInternal)
		}
		response := applicationresultsemantic.GetResultResponse{
			CommandID: request.CommandID, ExecutionRefDigest: executionRefDigest,
			State: applicationresultsemantic.ResultPending,
		}
		if applicationresultsemantic.ValidateGetResultResponse(request, response) != nil {
			return applicationresultsemantic.GetResultResponse{}, runtimePlatformApplicationResultFailure(commonsemantic.FailureInternal)
		}
		return response, nil
	case RuntimePlatformCommandCompleted, RuntimePlatformCommandFailed:
		if !encodedResult.Valid {
			return applicationresultsemantic.GetResultResponse{}, runtimePlatformApplicationResultFailure(commonsemantic.FailureInternal)
		}
	default:
		return applicationresultsemantic.GetResultResponse{}, runtimePlatformApplicationResultFailure(commonsemantic.FailureInternal)
	}

	resultPayload, err := decodeRuntimePlatformCommandResult(encodedResult.String)
	if err != nil || len(resultPayload) == 0 {
		return applicationresultsemantic.GetResultResponse{}, runtimePlatformApplicationResultFailure(commonsemantic.FailureInternal)
	}
	completion, err := decodeRuntimePlatformCommandApplicationCompletion(resultPayload)
	if err != nil || !runtimePlatformApplicationCompletionMatchesState(state, completion.Outcome) {
		return applicationresultsemantic.GetResultResponse{}, runtimePlatformApplicationResultFailure(commonsemantic.FailureInternal)
	}
	resultDigest, err := applicationresultsemantic.ComputeResultDigest(resultPayload)
	if err != nil {
		return applicationresultsemantic.GetResultResponse{}, runtimePlatformApplicationResultFailure(commonsemantic.FailureInternal)
	}
	result := applicationresultsemantic.ApplicationResult{
		CommandID: request.CommandID, ExecutionRefDigest: executionRefDigest,
		Outcome: completion.Outcome, ResultDigest: resultDigest, FailureCode: completion.FailureCode,
	}
	result.RuntimeCommitReceipt, err = applicationresultsemantic.ComputeRuntimeCommitReceipt(result)
	if err != nil {
		return applicationresultsemantic.GetResultResponse{}, runtimePlatformApplicationResultFailure(commonsemantic.FailureInternal)
	}
	response := applicationresultsemantic.GetResultResponse{
		CommandID: request.CommandID, ExecutionRefDigest: executionRefDigest,
		State: applicationresultsemantic.ResultTerminal, Result: &result,
	}
	if applicationresultsemantic.ValidateGetResultResponse(request, response) != nil {
		return applicationresultsemantic.GetResultResponse{}, runtimePlatformApplicationResultFailure(commonsemantic.FailureInternal)
	}
	return response, nil
}

func runtimePlatformApplicationCompletionMatchesState(
	state string,
	outcome applicationresultsemantic.Outcome,
) bool {
	switch state {
	case RuntimePlatformCommandCompleted:
		return outcome == applicationresultsemantic.OutcomeApplied || outcome == applicationresultsemantic.OutcomeNoop
	case RuntimePlatformCommandFailed:
		return outcome == applicationresultsemantic.OutcomeRejected || outcome == applicationresultsemantic.OutcomeFailed
	default:
		return false
	}
}

func runtimePlatformApplicationResultReadFailure(ctx context.Context) error {
	if ctx != nil {
		switch ctx.Err() {
		case context.Canceled:
			return runtimePlatformApplicationResultFailure(commonsemantic.FailureCanceled)
		case context.DeadlineExceeded:
			return runtimePlatformApplicationResultFailure(commonsemantic.FailureDeadlineExceeded)
		}
	}
	return runtimePlatformApplicationResultFailure(commonsemantic.FailureInternal)
}

func runtimePlatformApplicationResultFailure(category commonsemantic.FailureCategory) error {
	return &applicationresultsemantic.Failure{Category: category}
}
