package packs

import (
	"bytes"
	"context"
	"database/sql"
	"fmt"
	"sort"
	"strings"

	cp "github.com/pingcap-inc/nexuscrm-contract/pkg/controlplane"
)

// ManagedComponentHost retains the deprecated five-callback read surface for
// source compatibility. Mutation execution is disabled: active providers must
// bind the additive lifecycle and run through ManagedLifecycleHost.
type ManagedComponentHost struct {
	db              *sql.DB
	catalog         ComponentColumnCatalog
	kind            cp.ComponentKind
	owner           cp.ComponentOwnerStrategy
	manageabilities map[cp.Manageability]struct{}
	writeSet        []cp.ComponentWriteSurface
	provider        cp.ComponentManagedProvider
}

// NewManagedComponentHost validates and snapshots one managed registration.
func NewManagedComponentHost(db *sql.DB, catalog ComponentColumnCatalog, registration cp.ComponentKindRegistration) (*ManagedComponentHost, error) {
	if db == nil {
		return nil, fmt.Errorf("managed component database is required")
	}
	if catalog == nil {
		return nil, fmt.Errorf("managed component column catalog is required")
	}
	if err := validateManagedComponentRegistration(registration); err != nil {
		return nil, err
	}
	manageabilities := make(map[cp.Manageability]struct{}, len(registration.Contract.Manageabilities))
	for value := range registration.Contract.Manageabilities {
		manageabilities[value] = struct{}{}
	}
	return &ManagedComponentHost{
		db: db, catalog: catalog,
		kind: registration.Contract.Kind, owner: registration.Contract.OwnerDiscriminator,
		manageabilities: manageabilities,
		writeSet:        append([]cp.ComponentWriteSurface(nil), registration.Contract.WriteSet...),
		provider:        *registration.ManagedProvider,
	}, nil
}

func validateManagedComponentRegistration(registration cp.ComponentKindRegistration) error {
	if registration.ManagedProvider == nil {
		return fmt.Errorf("managed component %s legacy provider is required", registration.Contract.Kind)
	}
	if err := validateManagedComponentRegistrationShape(registration); err != nil {
		return err
	}
	if err := registration.ManagedProvider.Validate(); err != nil {
		return fmt.Errorf("managed component %s provider: %w", registration.Contract.Kind, err)
	}
	return nil
}

func sameComponentManageabilities(left, right map[cp.Manageability]struct{}) bool {
	if len(left) != len(right) {
		return false
	}
	for value := range left {
		if _, ok := right[value]; !ok || (value != cp.ManageabilityLocked && value != cp.ManageabilityDefaultsOnly) {
			return false
		}
	}
	return true
}

// Preflight runs collision checks against a fresh read-only snapshot.
func (h *ManagedComponentHost) Preflight(ctx context.Context, request cp.ComponentManagedRequest) error {
	if err := h.validateRequest(request); err != nil {
		return err
	}
	reader, err := newManagedComponentRowReader(h.db, h.catalog, h.writeSet)
	if err != nil {
		return err
	}
	if err := h.provider.Preflight(ctx, reader, request); err != nil {
		return fmt.Errorf("preflight managed component %s/%s: %w", request.Component.Kind, request.Component.BusinessKey, err)
	}
	return nil
}

// Apply fails closed before invoking the deprecated mutation callback.
func (h *ManagedComponentHost) Apply(ctx context.Context, request cp.ComponentManagedRequest) error {
	if err := h.validateRequest(request); err != nil {
		return err
	}
	return fmt.Errorf("legacy managed provider execution is disabled for %s/%s; bind an additive managed lifecycle", request.Component.Kind, request.Component.BusinessKey)
}

// VerifyProjection returns the provider's canonical expected and live bytes.
func (h *ManagedComponentHost) VerifyProjection(ctx context.Context, request cp.ComponentManagedRequest) ([]byte, []byte, error) {
	if err := h.validateRequest(request); err != nil {
		return nil, nil, err
	}
	reader, err := newManagedComponentRowReader(h.db, h.catalog, h.writeSet)
	if err != nil {
		return nil, nil, err
	}
	expected, live, err := h.provider.VerifyProjection(ctx, reader, request)
	if err != nil {
		return nil, nil, fmt.Errorf("verify managed component %s/%s: %w", request.Component.Kind, request.Component.BusinessKey, err)
	}
	return expected, live, nil
}

// ListLiveBusinessKeys returns a canonical, deterministic owner-scoped set.
func (h *ManagedComponentHost) ListLiveBusinessKeys(ctx context.Context, request cp.ComponentManagedSetRequest) ([]cp.ComponentLiveBusinessKey, error) {
	if err := h.validateSetRequest(request); err != nil {
		return nil, err
	}
	reader, err := newManagedComponentRowReader(h.db, h.catalog, h.writeSet)
	if err != nil {
		return nil, err
	}
	keys, err := h.provider.ListLiveBusinessKeys(ctx, reader, request)
	if err != nil {
		return nil, fmt.Errorf("list live managed component keys for %s: %w", request.Kind, err)
	}
	keys = append([]cp.ComponentLiveBusinessKey(nil), keys...)
	seen := make(map[string]struct{}, len(keys))
	for _, key := range keys {
		if err := validateCanonicalComponentIdentity("live business key", key.BusinessKey); err != nil {
			return nil, err
		}
		if _, exists := seen[key.BusinessKey]; exists {
			return nil, fmt.Errorf("live business key %q is duplicated", key.BusinessKey)
		}
		seen[key.BusinessKey] = struct{}{}
	}
	sort.Slice(keys, func(i, j int) bool { return keys[i].BusinessKey < keys[j].BusinessKey })
	return keys, nil
}

// ReconcileSet validates its compatibility inputs, then fails closed before
// invoking the deprecated mutation callback.
func (h *ManagedComponentHost) ReconcileSet(ctx context.Context, request cp.ComponentManagedSetRequest, declaredKeys []string) error {
	if err := h.validateSetRequest(request); err != nil {
		return err
	}
	if _, err := canonicalManagedComponentKeys(declaredKeys); err != nil {
		return err
	}
	return fmt.Errorf("legacy managed provider execution is disabled for %s/%s; bind an additive managed lifecycle", request.Kind, request.PackName)
}

func (h *ManagedComponentHost) validateRequest(request cp.ComponentManagedRequest) error {
	if err := validateCanonicalComponentIdentity("pack name", request.PackName); err != nil {
		return err
	}
	if request.Component.Kind != h.kind {
		return fmt.Errorf("managed component kind %q does not match host kind %q", request.Component.Kind, h.kind)
	}
	if err := validateCanonicalComponentIdentity("business key", request.Component.BusinessKey); err != nil {
		return err
	}
	if err := h.validateOwner(request.SourceOwner); err != nil {
		return err
	}
	if _, ok := h.manageabilities[request.Component.Manageability]; !ok {
		return fmt.Errorf("managed component %s does not support manageability %q", h.kind, request.Component.Manageability)
	}
	if len(request.Payload) == 0 || (len(request.Component.Payload) > 0 && !bytes.Equal(request.Component.Payload, request.Payload)) {
		return fmt.Errorf("managed component payload is required; an inline manifest payload must match it")
	}
	if len(request.ExpectedProjection) == 0 {
		return fmt.Errorf("managed component expected projection is required")
	}
	return nil
}

func (h *ManagedComponentHost) validateSetRequest(request cp.ComponentManagedSetRequest) error {
	if err := validateCanonicalComponentIdentity("pack name", request.PackName); err != nil {
		return err
	}
	if request.Kind != h.kind {
		return fmt.Errorf("managed component set kind %q does not match host kind %q", request.Kind, h.kind)
	}
	return h.validateOwner(request.SourceOwner)
}

func (h *ManagedComponentHost) validateOwner(owner string) error {
	switch h.owner {
	case cp.ComponentOwnerStrategyOwnerPack:
		if owner != "" {
			return fmt.Errorf("pack-owned managed component cannot carry source owner %q", owner)
		}
	case cp.ComponentOwnerStrategySource:
		if err := validateCanonicalComponentIdentity("source owner", owner); err != nil {
			return err
		}
	}
	return nil
}

func validateCanonicalComponentIdentity(label, value string) error {
	if value == "" || strings.TrimSpace(value) != value {
		return fmt.Errorf("managed component %s %q is not canonical", label, value)
	}
	return nil
}

func canonicalManagedComponentKeys(keys []string) ([]string, error) {
	out, seen := append([]string(nil), keys...), make(map[string]struct{}, len(keys))
	for _, key := range out {
		if err := validateCanonicalComponentIdentity("declared business key", key); err != nil {
			return nil, err
		}
		if _, exists := seen[key]; exists {
			return nil, fmt.Errorf("declared business key %q is duplicated", key)
		}
		seen[key] = struct{}{}
	}
	sort.Strings(out)
	return out, nil
}
