package controlplane

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	consumerv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
	connectorbindingownerv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/connectorbindingownerv1"
	"github.com/pingcap-inc/nexuscrm-controlplane/internal/connectorbindingdefinitionauthority"
	"github.com/pingcap-inc/nexuscrm-controlplane/packs"
)

// insertPackGenerationConnectorBindingDefinitionSet inserts an explicit parent
// commitment followed by every immutable child. It deliberately has no retry,
// repair, upsert, update, or delete behavior: exact retries take the read-only
// verification path before this writer is reached.
func insertPackGenerationConnectorBindingDefinitionSet(
	ctx context.Context,
	tx packGenerationActivationTransaction,
	generation sourceBackedPackGeneration,
	set connectorbindingownerv1.DefinitionSet,
	now time.Time,
) error {
	if err := validateDefinitionSetForPackGeneration(generation, set); err != nil {
		return err
	}
	header := set.Header
	if _, err := tx.ExecContext(ctx, `INSERT INTO _System_PackGenerationConnectorBindingDefinitionSet (
PackGenerationId, Protocol, DefinitionCount, DefinitionSetDigest,
CreatedDate, LastModifiedDate
) VALUES (?, ?, ?, ?, ?, ?)`,
		generation.id,
		header.Protocol,
		strconv.FormatUint(header.DefinitionCount, 10),
		header.OrderedSetDigest,
		now,
		now,
	); err != nil {
		return fmt.Errorf(
			"insert pack %q connector binding definition set: %w",
			generation.packName,
			err,
		)
	}

	for _, entry := range set.Entries {
		if _, err := tx.ExecContext(ctx, `INSERT INTO _System_PackGenerationConnectorBindingDefinition (
Id, PackGenerationId, PackInstallId, DefinitionOrdinal, Kind, OwnerPack,
PackPayloadDigest, BusinessKey, DefinitionVersion, DefinitionEnvelopeDigest,
DefinitionEnvelope, CreatedDate, LastModifiedDate
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
			packGenerationConnectorBindingDefinitionID(
				generation.id,
				entry.Kind,
				entry.BusinessKey,
			),
			generation.id,
			entry.PackInstallationID,
			strconv.FormatUint(entry.Ordinal, 10),
			string(entry.Kind),
			entry.OwnerPack,
			entry.PackPayloadDigest,
			entry.BusinessKey,
			strconv.FormatUint(entry.DefinitionVersion, 10),
			entry.EnvelopeDigest,
			entry.EnvelopeJSON,
			now,
			now,
		); err != nil {
			return fmt.Errorf(
				"insert pack %q connector binding definition %s/%s: %w",
				generation.packName,
				entry.Kind,
				entry.BusinessKey,
				err,
			)
		}
	}
	return nil
}

func verifyInsertedPackGenerationConnectorBindingDefinitionSet(
	ctx context.Context,
	tx packGenerationActivationTransaction,
	generation sourceBackedPackGeneration,
	expected connectorbindingownerv1.DefinitionSet,
) error {
	actual, err := readVerifiedPackGenerationConnectorBindingDefinitionSet(
		ctx,
		tx,
		generation,
	)
	if err != nil {
		return err
	}
	if !connectorBindingDefinitionSetsEqual(actual, expected) {
		return fmt.Errorf(
			"%w: pack %q stored connector binding definition set differs from the exact source publication",
			ErrPackGenerationConflict,
			generation.packName,
		)
	}
	return nil
}

func readVerifiedPackGenerationConnectorBindingDefinitionSet(
	ctx context.Context,
	tx packGenerationActivationTransaction,
	generation sourceBackedPackGeneration,
) (connectorbindingownerv1.DefinitionSet, error) {
	coordinate, err := connectorBindingDefinitionCoordinateForStoredGeneration(generation)
	if err != nil {
		return connectorbindingownerv1.DefinitionSet{}, err
	}
	set, err := connectorbindingdefinitionauthority.ReadVerifiedDefinitionSet(
		ctx,
		tx,
		connectorbindingdefinitionauthority.GenerationCoordinate{
			StorageID:          generation.id,
			OwnerPack:          coordinate.OwnerPack,
			PackInstallationID: coordinate.PackInstallationID,
			PackPayloadDigest:  coordinate.PackPayloadDigest,
		},
	)
	if err == nil {
		return set, nil
	}
	if errors.Is(err, connectorbindingdefinitionauthority.ErrPrecondition) {
		return connectorbindingownerv1.DefinitionSet{}, fmt.Errorf(
			"%w: %v",
			ErrPackGenerationPrecondition,
			err,
		)
	}
	if errors.Is(err, connectorbindingdefinitionauthority.ErrConflict) {
		return connectorbindingownerv1.DefinitionSet{}, fmt.Errorf(
			"%w: %v",
			ErrPackGenerationConflict,
			err,
		)
	}
	return connectorbindingownerv1.DefinitionSet{}, err
}

func addConnectorBindingDefinitionEnvelopeBytes(total uint64, envelopeSize int) (uint64, bool) {
	return connectorbindingdefinitionauthority.AddEnvelopeBytes(total, envelopeSize)
}

func validateDefinitionSetForPackGeneration(
	generation sourceBackedPackGeneration,
	set connectorbindingownerv1.DefinitionSet,
) error {
	coordinate, err := connectorBindingDefinitionCoordinateForStoredGeneration(generation)
	if err != nil {
		return err
	}
	if _, err := connectorbindingownerv1.ValidateDefinitionSet(set); err != nil {
		return fmt.Errorf(
			"%w: pack %q connector binding definition set is invalid: %v",
			ErrPackGenerationPrecondition,
			generation.packName,
			err,
		)
	}
	if set.Header.DefinitionSetCoordinate != coordinate {
		return fmt.Errorf(
			"%w: pack %q connector binding definition set is bound to a different generation",
			ErrPackGenerationPrecondition,
			generation.packName,
		)
	}
	return nil
}

func connectorBindingDefinitionCoordinateForStoredGeneration(
	generation sourceBackedPackGeneration,
) (connectorbindingownerv1.DefinitionSetCoordinate, error) {
	if generation.id != packGenerationID(generation.packName, generation.packInstallID) ||
		generation.packName == "" ||
		strings.TrimSpace(generation.packName) != generation.packName ||
		generation.packInstallID == "" ||
		strings.TrimSpace(generation.packInstallID) != generation.packInstallID ||
		!generation.appliedPayloadHash.Valid ||
		!packs.IsPackPayloadHashV1(generation.appliedPayloadHash.String) {
		return connectorbindingownerv1.DefinitionSetCoordinate{}, fmt.Errorf(
			"%w: pack %q stored generation cannot identify a connector binding definition set",
			ErrPackGenerationConflict,
			generation.packName,
		)
	}
	return connectorbindingownerv1.DefinitionSetCoordinate{
		OwnerPack:          generation.packName,
		PackInstallationID: generation.packInstallID,
		PackPayloadDigest:  generation.appliedPayloadHash.String,
	}, nil
}

func connectorBindingDefinitionSetsEqual(
	actual connectorbindingownerv1.DefinitionSet,
	expected connectorbindingownerv1.DefinitionSet,
) bool {
	if actual.Header != expected.Header || len(actual.Entries) != len(expected.Entries) {
		return false
	}
	for index := range actual.Entries {
		actualEntry := actual.Entries[index]
		expectedEntry := expected.Entries[index]
		if actualEntry.Ordinal != expectedEntry.Ordinal ||
			actualEntry.Kind != expectedEntry.Kind ||
			actualEntry.OwnerPack != expectedEntry.OwnerPack ||
			actualEntry.PackInstallationID != expectedEntry.PackInstallationID ||
			actualEntry.PackPayloadDigest != expectedEntry.PackPayloadDigest ||
			actualEntry.BusinessKey != expectedEntry.BusinessKey ||
			actualEntry.DefinitionVersion != expectedEntry.DefinitionVersion ||
			actualEntry.EnvelopeDigest != expectedEntry.EnvelopeDigest ||
			!bytes.Equal(actualEntry.EnvelopeJSON, expectedEntry.EnvelopeJSON) {
			return false
		}
	}
	return true
}

func verifyExistingPackGenerationConnectorBindingDefinitionSet(
	ctx context.Context,
	tx packGenerationActivationTransaction,
	applied appliedAppPack,
	expected sourceBackedPackGeneration,
	activation packActivation,
) error {
	actual, actualSet, err := verifyActivePackGenerationAndConnectorBindingDefinitions(
		ctx,
		tx,
		activation,
	)
	if err != nil {
		return err
	}
	if activation.id != packActivationID(expected.packName) ||
		activation.packName != expected.packName ||
		activation.activePackInstallID != expected.packInstallID ||
		!sourceBackedPackGenerationsEqual(actual, expected) {
		return fmt.Errorf(
			"%w: pack %q active generation does not match the verified source preimage",
			ErrPackGenerationConflict,
			expected.packName,
		)
	}

	// Rebuild the exact source-authored definitions with versions frozen by the
	// already validated stored set. A later mutable inbound-route repair must
	// not reinterpret this immutable generation as a different route version.
	expectedSet, err := sourceBackedPackConnectorBindingDefinitionSetAtFrozenVersions(
		applied,
		expected,
		actualSet,
	)
	if err != nil {
		return err
	}
	if !connectorBindingDefinitionSetsEqual(actualSet, expectedSet) {
		return fmt.Errorf(
			"%w: pack %q active connector binding definition set does not match the exact source preimage",
			ErrPackGenerationConflict,
			expected.packName,
		)
	}
	return nil
}

func verifyActivePackGenerationAndConnectorBindingDefinitions(
	ctx context.Context,
	tx packGenerationActivationTransaction,
	activation packActivation,
) (
	sourceBackedPackGeneration,
	connectorbindingownerv1.DefinitionSet,
	error,
) {
	if activation.id != packActivationID(activation.packName) ||
		strings.TrimSpace(activation.packName) == "" ||
		strings.TrimSpace(activation.activePackInstallID) == "" ||
		strings.TrimSpace(activation.activePackInstallID) != activation.activePackInstallID ||
		activation.activationEpoch < firstPackActivationEpoch ||
		!activation.activatedDate.Valid ||
		activation.isDeleted != 0 {
		return sourceBackedPackGeneration{}, connectorbindingownerv1.DefinitionSet{}, fmt.Errorf(
			"%w: pack %q activation is malformed",
			ErrPackGenerationConflict,
			activation.packName,
		)
	}
	if activation.previousPackInstallID.Valid {
		return sourceBackedPackGeneration{}, connectorbindingownerv1.DefinitionSet{}, fmt.Errorf(
			"%w: pack %q reserved previous-generation pointer must be null",
			ErrPackGenerationConflict,
			activation.packName,
		)
	}

	active, found, err := readPackGenerationForUpdate(
		ctx,
		tx,
		activation.activePackInstallID,
	)
	if err != nil {
		return sourceBackedPackGeneration{}, connectorbindingownerv1.DefinitionSet{}, err
	}
	if !found {
		return sourceBackedPackGeneration{}, connectorbindingownerv1.DefinitionSet{}, fmt.Errorf(
			"%w: pack %q active generation %q is missing",
			ErrPackGenerationConflict,
			activation.packName,
			activation.activePackInstallID,
		)
	}
	if err := validateStoredSourceBackedGeneration(active, activation.packName); err != nil {
		return sourceBackedPackGeneration{}, connectorbindingownerv1.DefinitionSet{}, err
	}
	set, err := readVerifiedPackGenerationConnectorBindingDefinitionSet(ctx, tx, active)
	if err != nil {
		return sourceBackedPackGeneration{}, connectorbindingownerv1.DefinitionSet{}, err
	}
	return active, set, nil
}

func packGenerationConnectorBindingDefinitionID(
	packGenerationID string,
	kind consumerv1.IntegrationBindingKind,
	businessKey string,
) string {
	return connectorbindingdefinitionauthority.DefinitionStorageID(
		packGenerationID,
		kind,
		businessKey,
	)
}
