package service

import (
	"context"
	"crypto/tls"
	"fmt"
	"os"
	"strings"

	"github.com/tidbcloud/cluster-service-ng/internal/cluster-agent/internal/sqlexec"
	"github.com/tidbcloud/cluster-service-ng/internal/cluster-agent/internal/utils"
)

type auditLogExecutor interface {
	ExecuteMulti(ctx context.Context, clusterID string, cmds []sqlexec.Command) error
}

var newAuditLogExecutor = func(
	resolver utils.AddressResolver,
	auth sqlexec.DBAuth,
	tlsProvider sqlexec.TLSConfigProvider,
) auditLogExecutor {
	return sqlexec.NewDirectTiDBExecutor(resolver, auth, tlsProvider)
}

func (s *AgentService) newAuditLogDirectExecutor(
	_ context.Context,
	encryptedUsername []byte,
	encryptedPassword []byte,
) (auditLogExecutor, error) {
	resolver, auth, tlsProvider, err := s.decryptDBCredentials(encryptedUsername, encryptedPassword)
	if err != nil {
		return nil, err
	}
	return newAuditLogExecutor(resolver, auth, tlsProvider), nil
}

func (s *AgentService) decryptDBCredentials(
	encryptedUsername []byte,
	encryptedPassword []byte,
) (utils.AddressResolver, sqlexec.DBAuth, sqlexec.TLSConfigProvider, error) {
	namespace := os.Getenv("NAMESPACE")
	if namespace == "" {
		return nil, sqlexec.DBAuth{}, nil, fmt.Errorf("NAMESPACE is empty")
	}
	if s.encryptor == nil {
		return nil, sqlexec.DBAuth{}, nil, fmt.Errorf("encryptor is nil")
	}

	usernameBytes, err := s.encryptor.Decrypt(encryptedUsername)
	if err != nil {
		return nil, sqlexec.DBAuth{}, nil, fmt.Errorf("failed to decrypt the username. ensure the public key is correct: %w", err)
	}
	passwordBytes, err := s.encryptor.Decrypt(encryptedPassword)
	if err != nil {
		return nil, sqlexec.DBAuth{}, nil, fmt.Errorf("failed to decrypt the password. ensure the public key is correct: %w", err)
	}

	auth := sqlexec.DBAuth{
		Username: string(usernameBytes),
		Password: sqlexec.NewSecretString(string(passwordBytes)),
	}
	resolver := utils.NewTiDBAddrResolver(s.dataplaneCli)
	tlsProvider := func(ctx context.Context, clusterID string) (*tls.Config, error) {
		_ = clusterID
		return GetTLSConfig(ctx, s.dataplaneCli, namespace, tlsClientSecret)
	}
	return resolver, auth, tlsProvider, nil
}

func validateAuditLogCommonFields(clusterID string, encryptedUsername, encryptedPassword []byte) error {
	if strings.TrimSpace(clusterID) == "" {
		return fmt.Errorf("cluster id is empty")
	}
	if len(encryptedUsername) == 0 {
		return fmt.Errorf("encrypted username is empty")
	}
	if len(encryptedPassword) == 0 {
		return fmt.Errorf("encrypted password is empty")
	}
	return nil
}
