package aifin

import (
	"context"
	"fmt"
	"log/slog"
	"net/http"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/extension/runner"
	"github.com/tidbcloud/AgentTeam/internal/extension/runner/mcp"
	"github.com/tidbcloud/AgentTeam/internal/observability/logging"
)

const (
	credentialValidationTimeout = 20 * time.Second
	credentialEndpointTimeout   = 15 * time.Second
	credentialCleanupTimeout    = 3 * time.Second
)

// ValidateCredentials independently negotiates and cleans one least-privilege
// Probe-only session against every used Wind endpoint. It invokes no tool or
// resource capability and retains no discovered metadata.
func ValidateCredentials(
	ctx context.Context,
	credentials runner.Credentials,
) error {
	clients, err := newProbeClients(nil)
	if err != nil {
		return runner.Errorf(
			runner.ErrorConnectionUnavailable,
			"Wind AIFin Market credential validation is unavailable",
			nil,
		)
	}
	return validateCredentials(ctx, clients, credentials)
}

func newProbeClients(transport http.RoundTripper) (mcpClients, error) {
	if err := validateRouting(); err != nil {
		return nil, err
	}
	clients := make(mcpClients, len(endpointPolicies))
	for _, policy := range endpointPolicies {
		client, err := mcp.NewClient(mcp.Options{
			Endpoint:       policy.Endpoint,
			Auth:           AuthSpec(),
			ProbeOnly:      true,
			Transport:      transport,
			Timeout:        credentialEndpointTimeout,
			CleanupTimeout: credentialCleanupTimeout,
		})
		if err != nil {
			return nil, fmt.Errorf(
				"wind AIFin Market: build %s probe client: %w",
				policy.Family,
				err,
			)
		}
		clients[policy.Family] = client
	}
	return clients, nil
}

func validateCredentials(
	ctx context.Context,
	clients mcpClients,
	credentials runner.Credentials,
) error {
	if ctx == nil {
		return runner.Errorf(
			runner.ErrorValidation,
			"Wind AIFin Market validation context is unavailable",
			nil,
		)
	}
	if len(clients) != len(endpointPolicies) {
		return runner.Errorf(
			runner.ErrorConnectionUnavailable,
			"Wind AIFin Market probe clients are incomplete",
			nil,
		)
	}
	for _, policy := range endpointPolicies {
		if clients[policy.Family] == nil {
			return runner.Errorf(
				runner.ErrorConnectionUnavailable,
				"Wind AIFin Market probe client is unavailable",
				nil,
			)
		}
	}

	validationCtx, cancel := context.WithTimeout(
		ctx,
		credentialValidationTimeout,
	)
	defer cancel()
	type probeResult struct {
		family Family
		err    error
	}
	results := make(chan probeResult, len(endpointPolicies))
	for _, policy := range endpointPolicies {
		family := policy.Family
		client := clients[family]
		logging.Go(slog.Default(), func() {
			results <- probeResult{
				family: family,
				err: client.Probe(
					validationCtx,
					credentials.Clone(),
				),
			}
		})
	}
	errorsByFamily := make(map[Family]error, len(endpointPolicies))
	for range endpointPolicies {
		result := <-results
		errorsByFamily[result.family] = result.err
	}
	for _, policy := range endpointPolicies {
		err := errorsByFamily[policy.Family]
		if runner.CodeOf(err) == runner.ErrorProviderAuthUnavailable {
			return err
		}
	}
	for _, policy := range endpointPolicies {
		if err := errorsByFamily[policy.Family]; err != nil {
			return err
		}
	}
	return nil
}
