package packs

import (
	"context"
	"fmt"

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

type sourceBackedExternalManagedComponent struct {
	packName          string
	manifestComponent ManifestComponent
	payload           []byte
	descriptor        cp.ComponentBuildManagedComponent
	registration      cp.ComponentKindRegistration
	lifecycle         *cp.ComponentManagedLifecycleRegistration
	effects           []ManagedLifecycleEffectAdapter
}

var (
	_ PackComponent                     = (*sourceBackedExternalManagedComponent)(nil)
	_ componentInstallPlanPhaseProvider = (*sourceBackedExternalManagedComponent)(nil)
)

func newSourceBackedExternalManagedComponent(packName string, manifest ManifestComponent, payload []byte, descriptor cp.ComponentBuildManagedComponent, registration cp.ComponentKindRegistration, lifecycles ...cp.ComponentManagedLifecycleRegistration) *sourceBackedExternalManagedComponent {
	manifest.DependsOn = append([]ComponentReference(nil), manifest.DependsOn...)
	manifest.Payload = append([]byte(nil), manifest.Payload...)
	descriptor.ExpectedProjection = append([]byte(nil), descriptor.ExpectedProjection...)
	descriptor.CollisionClaims = append([]cp.ComponentCollisionClaim(nil), descriptor.CollisionClaims...)
	component := &sourceBackedExternalManagedComponent{
		packName: packName, manifestComponent: manifest, payload: append([]byte(nil), payload...),
		descriptor: descriptor, registration: cloneComponentKindRegistration(registration),
	}
	if len(lifecycles) == 1 {
		lifecycle := lifecycles[0]
		component.lifecycle = &lifecycle
	}
	return component
}

func (c *sourceBackedExternalManagedComponent) Kind() ComponentKind { return c.manifestComponent.Kind }
func (c *sourceBackedExternalManagedComponent) PackName() string    { return c.packName }
func (c *sourceBackedExternalManagedComponent) BusinessKey() string {
	return c.manifestComponent.BusinessKey
}

func (c *sourceBackedExternalManagedComponent) Manageability() Manageability {
	return c.manifestComponent.Manageability
}

func (c *sourceBackedExternalManagedComponent) DependsOn() []ComponentReference {
	return append([]ComponentReference(nil), c.manifestComponent.DependsOn...)
}

func (c *sourceBackedExternalManagedComponent) ExpectedPayload() ([]byte, error) {
	return append([]byte(nil), c.payload...), nil
}

func (c *sourceBackedExternalManagedComponent) ExpectedProjection() (any, error) {
	return append([]byte(nil), c.descriptor.ExpectedProjection...), nil
}

func (c *sourceBackedExternalManagedComponent) LiveProjection(context.Context, VerifyDeps) (any, error) {
	return nil, c.lifecycleNotWiredError()
}

func (c *sourceBackedExternalManagedComponent) Apply(context.Context, ApplyDeps) error {
	return c.lifecycleNotWiredError()
}

func (c *sourceBackedExternalManagedComponent) VerifyComponent(ctx context.Context, deps VerifyDeps) error {
	request, err := c.managedRequest()
	if err != nil {
		return err
	}
	if c.lifecycle != nil {
		host, err := NewManagedLifecycleHost(deps.DB, c.registration, *c.lifecycle, c.effects)
		if err != nil {
			return fmt.Errorf("prepare managed lifecycle %s/%s verification: %w", c.Kind(), c.BusinessKey(), err)
		}
		verification, err := host.VerifyItem(ctx, request)
		if err != nil {
			return err
		}
		if verification.Matches {
			return nil
		}
		return fmt.Errorf("managed lifecycle %s/%s projection drift: expected %q, live %q", c.Kind(), c.BusinessKey(), verification.Projection.Expected, verification.Projection.Live)
	}
	return fmt.Errorf("legacy managed provider verification is disabled for %s/%s; bind an additive managed lifecycle", c.Kind(), c.BusinessKey())
}

func (c *sourceBackedExternalManagedComponent) managedRequest() (cp.ComponentManagedRequest, error) {
	switch c.registration.Contract.OwnerDiscriminator {
	case cp.ComponentOwnerStrategyOwnerPack:
	case cp.ComponentOwnerStrategySource:
		return cp.ComponentManagedRequest{}, fmt.Errorf(
			"source-owned managed component %s/%s has no explicit source owner",
			c.Kind(), c.BusinessKey(),
		)
	default:
		return cp.ComponentManagedRequest{}, fmt.Errorf(
			"managed component %s/%s has unsupported owner discriminator %q",
			c.Kind(), c.BusinessKey(), c.registration.Contract.OwnerDiscriminator,
		)
	}
	return cp.ComponentManagedRequest{
		PackName:           c.packName,
		Component:          contractManifestComponent(c.manifestComponent, c.payload),
		Payload:            append([]byte(nil), c.payload...),
		ExpectedProjection: append([]byte(nil), c.descriptor.ExpectedProjection...),
	}, nil
}

func cloneManagedComponentRequest(request cp.ComponentManagedRequest) cp.ComponentManagedRequest {
	request.Component.Payload = append([]byte(nil), request.Component.Payload...)
	request.Component.DependsOn = append([]cp.ComponentReference(nil), request.Component.DependsOn...)
	request.Payload = append([]byte(nil), request.Payload...)
	request.ExpectedProjection = append([]byte(nil), request.ExpectedProjection...)
	return request
}

func (c *sourceBackedExternalManagedComponent) installPlanPhase() InstallPlanPhase {
	return InstallPlanPhase(c.registration.Contract.Phase)
}

func (c *sourceBackedExternalManagedComponent) lifecycleNotWiredError() error {
	return fmt.Errorf("managed component lifecycle is not wired for %s/%s", c.Kind(), c.BusinessKey())
}
