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

package httpserver

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	check "gopkg.in/check.v1"
)

func (s *Suite) TestInspect(c *check.C) {
	reg := prometheus.NewRegistry()
	h := newTestHandler()
	mh := Inspect(reg, "abcd", h)
	handlerReturned := make(chan struct{})
	reqctx, reqcancel := context.WithCancel(context.Background())
	longreq := httptest.NewRequest("GET", "/test", nil).WithContext(reqctx)
	go func() {
		mh.ServeHTTP(httptest.NewRecorder(), longreq)
		close(handlerReturned)
	}()
	<-h.inHandler

	resp := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/_inspect/requests", nil)
	mh.ServeHTTP(resp, req)
	c.Check(resp.Code, check.Equals, http.StatusUnauthorized)
	c.Check(resp.Body.String(), check.Equals, `{"errors":["unauthorized"]}`+"\n")

	resp = httptest.NewRecorder()
	req.Header.Set("Authorization", "Bearer abcde")
	mh.ServeHTTP(resp, req)
	c.Check(resp.Code, check.Equals, http.StatusUnauthorized)

	resp = httptest.NewRecorder()
	req.Header.Set("Authorization", "Bearer abcd")
	mh.ServeHTTP(resp, req)
	c.Check(resp.Code, check.Equals, http.StatusOK)
	reqs := []map[string]interface{}{}
	err := json.NewDecoder(resp.Body).Decode(&reqs)
	c.Check(err, check.IsNil)
	c.Check(reqs, check.HasLen, 1)
	c.Check(reqs[0]["URL"], check.Equals, "/test")

	// Request is active, so we should see active request age > 0
	resp = httptest.NewRecorder()
	mreq := httptest.NewRequest("GET", "/metrics", nil)
	promhttp.HandlerFor(reg, promhttp.HandlerOpts{}).ServeHTTP(resp, mreq)
	c.Check(resp.Code, check.Equals, http.StatusOK)
	c.Check(resp.Body.String(), check.Matches, `(?ms).*\narvados_max_active_request_age_seconds [0\.]*[1-9][-\d\.e]*\n.*`)
	c.Check(resp.Body.String(), check.Matches, `(?ms).*\narvados_max_abandoned_request_age_seconds 0\n.*`)

	reqcancel()

	// Request context is canceled but handler hasn't returned, so
	// we should see max abandoned request age > 0 and active ==
	// 0. We might need to wait a short time for the cancel to
	// propagate.
	for deadline := time.Now().Add(time.Second); time.Now().Before(deadline); time.Sleep(time.Second / 100) {
		resp = httptest.NewRecorder()
		promhttp.HandlerFor(reg, promhttp.HandlerOpts{}).ServeHTTP(resp, mreq)
		c.Assert(resp.Code, check.Equals, http.StatusOK)
		if strings.Contains(resp.Body.String(), "\narvados_max_active_request_age_seconds 0\n") {
			break
		}
	}
	c.Check(resp.Body.String(), check.Matches, `(?ms).*\narvados_max_active_request_age_seconds 0\n.*`)
	c.Check(resp.Body.String(), check.Matches, `(?ms).*\narvados_max_abandoned_request_age_seconds [0\.]*[1-9][-\d\.e]*\n.*`)

	h.okToProceed <- struct{}{}
	<-handlerReturned

	// Handler has returned, so we should see max abandoned
	// request age == max active request age == 0
	resp = httptest.NewRecorder()
	promhttp.HandlerFor(reg, promhttp.HandlerOpts{}).ServeHTTP(resp, mreq)
	c.Check(resp.Code, check.Equals, http.StatusOK)
	c.Check(resp.Body.String(), check.Matches, `(?ms).*\narvados_max_active_request_age_seconds 0\n.*`)
	c.Check(resp.Body.String(), check.Matches, `(?ms).*\narvados_max_abandoned_request_age_seconds 0\n.*`)

	// ...and no active requests at the /_monitor endpoint
	resp = httptest.NewRecorder()
	mh.ServeHTTP(resp, req)
	c.Check(resp.Code, check.Equals, http.StatusOK)
	reqs = nil
	err = json.NewDecoder(resp.Body).Decode(&reqs)
	c.Check(err, check.IsNil)
	c.Assert(reqs, check.HasLen, 0)
}
