// Copyright (C) The Arvados Authors. All rights reserved.
//
// SPDX-License-Identifier: AGPL-3.0

package federation

import (
	"context"
	"fmt"
	"net/http"

	"git.arvados.org/arvados.git/lib/ctrlctx"
	"git.arvados.org/arvados.git/sdk/go/arvados"
	"git.arvados.org/arvados.git/sdk/go/arvadostest"
	"git.arvados.org/arvados.git/sdk/go/auth"
	"git.arvados.org/arvados.git/sdk/go/ctxlog"
	"git.arvados.org/arvados.git/sdk/go/httpserver"
	check "gopkg.in/check.v1"
)

var _ = check.Suite(&collectionSuite{})

type collectionSuite struct {
	FederationSuite
}

func (s *collectionSuite) TestMultipleBackendFailureStatus(c *check.C) {
	nxPDH := "a4f995dd0c08216f37cb1bdec990f0cd+1234"
	s.cluster.ClusterID = "local"
	for _, trial := range []struct {
		label        string
		token        string
		localStatus  int
		remoteStatus map[string]int
		expectStatus int
	}{
		{
			"all backends return 404 => 404",
			arvadostest.SystemRootToken,
			http.StatusNotFound,
			map[string]int{
				"aaaaa": http.StatusNotFound,
				"bbbbb": http.StatusNotFound,
			},
			http.StatusNotFound,
		},
		{
			"all backends return 401 => 401 (e.g., bad token)",
			arvadostest.SystemRootToken,
			http.StatusUnauthorized,
			map[string]int{
				"aaaaa": http.StatusUnauthorized,
				"bbbbb": http.StatusUnauthorized,
			},
			http.StatusUnauthorized,
		},
		{
			"local 404, remotes 403 => 422 (mix of non-retryable errors)",
			arvadostest.SystemRootToken,
			http.StatusNotFound,
			map[string]int{
				"aaaaa": http.StatusForbidden,
				"bbbbb": http.StatusForbidden,
			},
			http.StatusUnprocessableEntity,
		},
		{
			"local 404, remotes 401/403/404 => 422 (mix of non-retryable errors)",
			arvadostest.SystemRootToken,
			http.StatusNotFound,
			map[string]int{
				"aaaaa": http.StatusUnauthorized,
				"bbbbb": http.StatusForbidden,
				"ccccc": http.StatusNotFound,
			},
			http.StatusUnprocessableEntity,
		},
		{
			"local 404, remotes 401/403/500 => 502 (at least one remote is retryable)",
			arvadostest.SystemRootToken,
			http.StatusNotFound,
			map[string]int{
				"aaaaa": http.StatusUnauthorized,
				"bbbbb": http.StatusForbidden,
				"ccccc": http.StatusInternalServerError,
			},
			http.StatusBadGateway,
		},
	} {
		c.Logf("trial: %v", trial)
		s.fed = New(s.ctx, s.cluster, nil, (&ctrlctx.DBConnector{PostgreSQL: s.cluster.PostgreSQL}).GetDB)
		s.fed.local = &arvadostest.APIStub{Error: httpserver.ErrorWithStatus(fmt.Errorf("stub error %d", trial.localStatus), trial.localStatus)}
		for id, status := range trial.remoteStatus {
			s.addDirectRemote(c, id, &arvadostest.APIStub{Error: httpserver.ErrorWithStatus(fmt.Errorf("stub error %d", status), status)})
		}

		ctx := context.Background()
		ctx = ctxlog.Context(ctx, ctxlog.TestLogger(c))
		if trial.token != "" {
			ctx = auth.NewContext(ctx, &auth.Credentials{Tokens: []string{trial.token}})
		}

		_, err := s.fed.CollectionGet(s.ctx, arvados.GetOptions{UUID: nxPDH})
		c.Check(err.(httpserver.HTTPStatusError).HTTPStatus(), check.Equals, trial.expectStatus)
	}
}