package connector_health

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

	"github.com/pingcap-inc/nexuscrm-connectors/features/connector/callout"
	"github.com/pingcap-inc/nexuscrm-connectors/features/namedcredential"
)

// CredentialResolver is the NamedCredential service surface needed by the
// connector health view.
type CredentialResolver interface {
	HasRequiredSecrets(ctx context.Context, devName string) (bool, error)
}

// CredentialStatusStore computes connector-level credential validity from the
// active callout profiles authored by a connector pack.
type CredentialStatusStore struct {
	db    *sql.DB
	creds CredentialResolver
}

func NewCredentialStatusStore(db *sql.DB, creds CredentialResolver) *CredentialStatusStore {
	return &CredentialStatusStore{db: db, creds: creds}
}

// CredentialValid returns true only when the active connector pack has at least
// one live callout profile and every referenced NamedCredential has usable
// required secrets for its AuthType. Missing definitions, inactive pack-owned
// definitions, Custom auth, non-converged active packs, cross-install profile
// drift, missing or undecryptable required secrets, and empty profile sets fail
// closed.
func (s *CredentialStatusStore) CredentialValid(ctx context.Context, namespace string) (bool, error) {
	namespace = strings.TrimSpace(namespace)
	if namespace == "" || s == nil || s.db == nil || s.creds == nil {
		return false, nil
	}
	installID, found, err := activeConnectorPackInstallID(ctx, s.db, namespace)
	if err != nil {
		return false, err
	}
	if !found {
		return false, nil
	}
	profiles, err := connector_callout.LoadProfilesForInboundWorker(ctx, s.db, namespace, installID, connector_callout.BatchProfileReadActiveOnly)
	if err != nil {
		if errors.Is(err, connector_callout.ErrProfileUnavailable) {
			return false, nil
		}
		return false, err
	}

	refs := make(map[string]struct{})
	for _, profile := range profiles {
		if profile == nil {
			continue
		}
		addCredentialRef(refs, profile.CredentialRef)
		addCredentialRef(refs, profile.BatchSpec.CredentialRef)
	}
	if len(profiles) == 0 || len(refs) == 0 {
		return false, nil
	}
	for credentialRef := range refs {
		ok, err := s.creds.HasRequiredSecrets(ctx, credentialRef)
		if err != nil {
			return false, err
		}
		if !ok {
			return false, nil
		}
	}
	return true, nil
}

func activeConnectorPackInstallID(ctx context.Context, db *sql.DB, ownerPack string) (string, bool, error) {
	rows, err := db.QueryContext(ctx, `
SELECT PackInstallId
FROM _System_InstalledPack
WHERE Name = ?
  AND Category = 'connector-pack'
  AND Status = ?
  AND TRIM(COALESCE(AppliedPayloadHash, '')) <> ''
  AND TRIM(COALESCE(DesiredPayloadHash, '')) <> ''
  AND AppliedPayloadHash = DesiredPayloadHash
  AND COALESCE(IsDeleted, 0) = 0`, ownerPack, "active")
	if err != nil {
		return "", false, err
	}
	defer rows.Close()

	var installID string
	count := 0
	for rows.Next() {
		var raw sql.NullString
		if err := rows.Scan(&raw); err != nil {
			return "", false, err
		}
		next := strings.TrimSpace(raw.String)
		if next == "" {
			return "", false, nil
		}
		installID = next
		count++
	}
	if err := rows.Err(); err != nil {
		return "", false, err
	}
	if count != 1 {
		return "", false, nil
	}
	return installID, true, nil
}

func addCredentialRef(refs map[string]struct{}, credentialRef string) {
	credentialRef = strings.TrimSpace(credentialRef)
	if credentialRef != "" {
		refs[credentialRef] = struct{}{}
	}
}

var _ CredentialResolver = (*namedcredential.Service)(nil)
