package catalogreconcile

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

	"github.com/nexuscrm/db/pkg/connectorreaders"
	"github.com/nexuscrm/metadata/catalogsnapshot"
	consumerv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
	connector "github.com/pingcap-inc/nexuscrm-contract/pkg/connector"
	"github.com/pingcap-inc/nexuscrm-controlplane/catalogapp"
	"github.com/pingcap-inc/nexuscrm-controlplane/packs"
)

func (r *RepositoryMetadataReader) GetAllInboundConnectorFieldMappings(
	ctx context.Context,
) ([]connector.ConnectorFieldMappingInput, error) {
	return connectorreaders.GetAllInboundConnectorFieldMappingsContext(
		ctx,
		r.GetExecutor(ctx),
	)
}

func (r *RepositoryMetadataReader) GetAllPackInboundConnectorFieldMappingsForOwner(
	ctx context.Context,
	owner string,
) ([]connector.ConnectorFieldMappingInput, error) {
	return connectorreaders.GetAllPackInboundConnectorFieldMappingsForOwnerContext(
		ctx,
		r.GetExecutor(ctx),
		owner,
	)
}

func (r *RepositoryMetadataReader) ResolveConnectorFieldMappings(
	ctx context.Context,
	binding consumerv1.IntegrationBinding,
) (ConnectorFieldMappingSelection, error) {
	if binding.Kind != consumerv1.BindingConnectorFieldMapping {
		return ConnectorFieldMappingSelection{}, fmt.Errorf(
			"integration binding kind %q is not a connector field mapping",
			binding.Kind,
		)
	}
	executor := r.GetExecutor(ctx)
	if err := catalogapp.VerifyIntegrationPackAuthority(ctx, executor, binding); err != nil {
		return ConnectorFieldMappingSelection{}, fmt.Errorf(
			"read exact connector mapping generation: %w",
			err,
		)
	}
	if err := ctx.Err(); err != nil {
		return ConnectorFieldMappingSelection{}, err
	}
	declaration, err := readExactConnectorRuntimeDeclaration(ctx, executor, binding)
	if err != nil {
		return ConnectorFieldMappingSelection{}, err
	}
	inbound, outbound, err := packs.LoadPersistedConnectorFieldMappingDirections(
		declaration,
		binding.OwnerPack,
		binding.BusinessKey,
	)
	if err != nil {
		return ConnectorFieldMappingSelection{}, err
	}
	return ConnectorFieldMappingSelection{
		Generation:       binding.PackInstallationID,
		Mappings:         inbound,
		OutboundMappings: outbound,
	}, nil
}

func (r *RepositoryMetadataReader) ResolveConnectorPushContract(
	ctx context.Context,
	binding consumerv1.IntegrationBinding,
) (ConnectorPushContractSelection, error) {
	if binding.Kind != consumerv1.BindingConnectorPushContract {
		return ConnectorPushContractSelection{}, fmt.Errorf(
			"integration binding kind %q is not a connector push contract",
			binding.Kind,
		)
	}
	executor := r.GetExecutor(ctx)
	if err := catalogapp.VerifyIntegrationPackAuthority(ctx, executor, binding); err != nil {
		return ConnectorPushContractSelection{}, fmt.Errorf(
			"read exact connector binding generation: %w",
			err,
		)
	}
	if err := ctx.Err(); err != nil {
		return ConnectorPushContractSelection{}, err
	}

	declaration, err := readExactConnectorRuntimeDeclaration(ctx, executor, binding)
	if err != nil {
		return ConnectorPushContractSelection{}, err
	}
	contract, err := packs.LoadPersistedConnectorPushContract(
		declaration,
		binding.BusinessKey,
	)
	if err != nil {
		return ConnectorPushContractSelection{}, err
	}
	eligibility, err := packs.LoadPersistedConnectorPushRecoverySourceEligibility(
		declaration,
		binding.BusinessKey,
	)
	if err != nil {
		return ConnectorPushContractSelection{}, err
	}
	if contract.ConnectorOwnerKey != binding.OwnerPack {
		return ConnectorPushContractSelection{}, fmt.Errorf(
			"connector push contract owner %q disagrees with binding owner %q",
			contract.ConnectorOwnerKey,
			binding.OwnerPack,
		)
	}
	return ConnectorPushContractSelection{
		Generation:                binding.PackInstallationID,
		Contract:                  contract,
		RecoverySourceEligibility: eligibility,
	}, nil
}

func (r *RepositoryMetadataReader) ResolveConnectorCalloutProfile(
	ctx context.Context,
	binding consumerv1.IntegrationBinding,
) (ConnectorCalloutProfileSelection, error) {
	if binding.Kind != consumerv1.BindingConnectorCalloutProfile {
		return ConnectorCalloutProfileSelection{}, fmt.Errorf(
			"integration binding kind %q is not a connector callout profile",
			binding.Kind,
		)
	}
	executor := r.GetExecutor(ctx)
	canonicalDefinition, err := catalogapp.ReadIntegrationPackAuthorityDefinition(
		ctx,
		executor,
		binding,
	)
	if err != nil {
		return ConnectorCalloutProfileSelection{}, fmt.Errorf(
			"read exact connector profile generation: %w",
			err,
		)
	}
	if err := ctx.Err(); err != nil {
		return ConnectorCalloutProfileSelection{}, err
	}
	declaration, err := readExactConnectorRuntimeDeclaration(ctx, executor, binding)
	if err != nil {
		return ConnectorCalloutProfileSelection{}, err
	}
	profile, err := packs.LoadPersistedConnectorCalloutProfileFinalization(
		declaration,
		binding.OwnerPack,
		binding.BusinessKey,
	)
	if err != nil {
		return ConnectorCalloutProfileSelection{}, err
	}
	if !slices.Contains(binding.CredentialRefs, profile.CredentialRef) {
		return ConnectorCalloutProfileSelection{}, fmt.Errorf(
			"connector callout profile %s/%s credential %q is not authorized by its Integration binding",
			binding.OwnerPack,
			binding.BusinessKey,
			profile.CredentialRef,
		)
	}
	outboundCapabilities, err := packs.LoadConnectorCalloutProfileOutboundCapabilities(
		canonicalDefinition,
		binding.OwnerPack,
		binding.BusinessKey,
	)
	if err != nil {
		return ConnectorCalloutProfileSelection{}, fmt.Errorf(
			"read exact connector profile outbound authority: %w",
			err,
		)
	}
	return ConnectorCalloutProfileSelection{
		Generation:           binding.PackInstallationID,
		OutboundCapabilities: outboundCapabilities,
		Profile: catalogsnapshot.ConnectorInboundFinalizationProfile{
			Generation:           binding.PackInstallationID,
			OwnerPack:            profile.OwnerPack,
			ProfileName:          profile.ProfileName,
			CredentialRef:        profile.CredentialRef,
			EndpointClass:        profile.EndpointClass,
			MaxAttempts:          profile.MaxAttempts,
			BaseDelayNanos:       profile.BaseDelayNanos,
			Multiplier:           profile.Multiplier,
			MaxDelayNanos:        profile.MaxDelayNanos,
			RetryableStatusCodes: slices.Clone(profile.RetryableStatusCodes),
		},
	}, nil
}

func readExactConnectorRuntimeDeclaration(
	ctx context.Context,
	executor interface {
		QueryRowContext(context.Context, string, ...interface{}) *sql.Row
	},
	binding consumerv1.IntegrationBinding,
) (string, error) {
	var declaration sql.NullString
	err := executor.QueryRowContext(ctx, `
SELECT g.RuntimeStartupShallowDeclarations
FROM _System_PackGeneration AS g
WHERE g.PackName = ?
  AND g.PackInstallId = ?
  AND g.Status = 'active'
  AND g.AppliedPayloadHash = ?
  AND g.DesiredPayloadHash = g.AppliedPayloadHash
  AND COALESCE(g.IsDeleted, 0) = 0
LIMIT 1
FOR UPDATE`,
		binding.OwnerPack,
		binding.PackInstallationID,
		binding.PackPayloadDigest,
	).Scan(&declaration)
	if errors.Is(err, sql.ErrNoRows) {
		return "", fmt.Errorf(
			"connector binding %s/%s has no matching immutable pack generation",
			binding.OwnerPack,
			binding.BusinessKey,
		)
	}
	if err != nil {
		return "", fmt.Errorf(
			"read connector binding immutable Runtime declaration: %w",
			err,
		)
	}
	if !declaration.Valid || strings.TrimSpace(declaration.String) == "" {
		return "", fmt.Errorf(
			"connector binding %s/%s immutable pack generation is missing its Runtime declaration",
			binding.OwnerPack,
			binding.BusinessKey,
		)
	}
	return declaration.String, nil
}

var _ ConnectorExecutionMetadataReader = (*RepositoryMetadataReader)(nil)
