package persistence

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

	flowcommandsemanticv3 "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/flowcommandv3"
)

const runtimePlatformApplicationResultReadinessQuery = `SELECT Id, State, Payload, ResultPayload
FROM _System_RuntimePlatformCommand
WHERE ExecutionScopeDigest = ?
  AND OrgId = ? AND AppInstallationId = ? AND RuntimeCapabilityId = ?
  AND CommandKind = ? AND PayloadFormatVersion = ?
ORDER BY Id`

// verifyRuntimePlatformApplicationResults rejects partially represented v3
// application results before the worker becomes ready. It deliberately ignores
// retained v1/v2 rows: only Flow command v3 has the ApplicationResult contract.
func verifyRuntimePlatformApplicationResults(
	ctx context.Context,
	db *sql.DB,
	scope RuntimeWorkScope,
) error {
	if ctx == nil || db == nil {
		return errors.New("runtime platform application result readiness: context and database are required")
	}
	scopeDigest, err := scope.Digest()
	if err != nil {
		return fmt.Errorf("runtime platform application result readiness: invalid work scope: %w", err)
	}
	rows, err := db.QueryContext(
		ctx,
		runtimePlatformApplicationResultReadinessQuery,
		scopeDigest,
		scope.OrgID,
		scope.AppInstallationID,
		scope.RuntimeCapabilityID,
		flowcommandsemanticv3.CommandKind,
		flowcommandsemanticv3.PayloadFormatVersion,
	)
	if err != nil {
		return fmt.Errorf("runtime platform application result readiness: query v3 commands: %w", err)
	}
	defer rows.Close()

	for rows.Next() {
		var rowID, state string
		var payload []byte
		var encodedResult sql.NullString
		if err := rows.Scan(&rowID, &state, &payload, &encodedResult); err != nil {
			return fmt.Errorf("runtime platform application result readiness: scan v3 command: %w", err)
		}
		if _, err := flowcommandsemanticv3.Decode(payload); err != nil {
			return fmt.Errorf(
				"runtime platform application result readiness: v3 command %q has an invalid payload: %w",
				rowID,
				err,
			)
		}

		switch state {
		case RuntimePlatformCommandAccepted,
			RuntimePlatformCommandProcessing,
			RuntimePlatformCommandBlocked,
			RuntimePlatformCommandPoisoned:
			if encodedResult.Valid {
				return fmt.Errorf(
					"runtime platform application result readiness: pending v3 command %q has a terminal result",
					rowID,
				)
			}
		case RuntimePlatformCommandCompleted, RuntimePlatformCommandFailed:
			if !encodedResult.Valid {
				return fmt.Errorf(
					"runtime platform application result readiness: terminal v3 command %q has no result",
					rowID,
				)
			}
			result, err := decodeRuntimePlatformCommandResult(encodedResult.String)
			if err != nil {
				return fmt.Errorf(
					"runtime platform application result readiness: v3 command %q has an invalid result encoding: %w",
					rowID,
					err,
				)
			}
			completion, err := decodeRuntimePlatformCommandApplicationCompletion(result)
			if err != nil {
				return fmt.Errorf(
					"runtime platform application result readiness: v3 command %q has an invalid completion: %w",
					rowID,
					err,
				)
			}
			if !runtimePlatformApplicationCompletionMatchesState(state, completion.Outcome) {
				return fmt.Errorf(
					"runtime platform application result readiness: v3 command %q has completion outcome %q in state %q",
					rowID,
					completion.Outcome,
					state,
				)
			}
		default:
			return fmt.Errorf(
				"runtime platform application result readiness: v3 command %q has unsupported state %q",
				rowID,
				state,
			)
		}
	}
	if err := rows.Err(); err != nil {
		return fmt.Errorf("runtime platform application result readiness: iterate v3 commands: %w", err)
	}
	return nil
}
