package fuphttp_test

import (
	"context"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"

	"hg.lukegb.com/lukegb/depot/web/fup/fuphttp"
)

func TestTokenAuthMiddlewareNoToken(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	t.Cleanup(cancel)

	ccfg := *cfg
	ccfg.AuthMiddleware = fuphttp.TokenAuthMiddleware("", "realm")

	a, err := fuphttp.New(ctx, &ccfg)
	if err != nil {
		t.Fatalf("fuphttp.New: %v", err)
	}
	s := httptest.NewServer(a.Handler())
	t.Cleanup(s.Close)

	resp, err := s.Client().Get(s.URL)
	if err != nil {
		t.Fatalf("Get(%q): %v", s.URL, err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		t.Errorf("status code was %v; want %v", resp.StatusCode, http.StatusOK)
	}
}

func TestTokenAuthMiddleware(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	t.Cleanup(cancel)

	ccfg := *cfg
	ccfg.AuthMiddleware = fuphttp.TokenAuthMiddleware("token", "realm")

	a, err := fuphttp.New(ctx, &ccfg)
	if err != nil {
		t.Fatalf("fuphttp.New: %v", err)
	}
	s := httptest.NewServer(a.Handler())
	t.Cleanup(s.Close)

	tcs := []struct {
		name        string
		path        string
		password    string
		headerToken string
		wantStatus  int
		wantText    string
	}{{
		name:       "root, no creds",
		path:       "/",
		wantStatus: http.StatusUnauthorized,
		wantText:   "unparsable or no credentials\n",
	}, {
		name:       "root, with bad creds",
		path:       "/",
		password:   "wrong password",
		wantStatus: http.StatusUnauthorized,
		wantText:   "bad credentials\n",
	}, {
		name:       "root, with good creds",
		path:       "/",
		password:   "token",
		wantStatus: http.StatusOK,
	}, {
		name:        "root, with good creds as header",
		path:        "/",
		headerToken: "token",
		wantStatus:  http.StatusOK,
	}, {
		name:       "raw",
		path:       "/raw/foo.txt",
		wantStatus: http.StatusNotFound,
	}, {
		name:       "raw, with bad creds",
		path:       "/raw/foo.txt",
		password:   "wrong password",
		wantStatus: http.StatusNotFound,
	}, {
		name:       "raw, with good creds",
		path:       "/raw/foo.txt",
		password:   "token",
		wantStatus: http.StatusNotFound,
	}, {
		name:       "pretty",
		path:       "/foo.txt",
		wantStatus: http.StatusNotFound,
	}, {
		name:       "pretty, with bad creds",
		path:       "/foo.txt",
		password:   "wrong password",
		wantStatus: http.StatusNotFound,
	}, {
		name:       "pretty, with good creds",
		path:       "/foo.txt",
		password:   "token",
		wantStatus: http.StatusNotFound,
	}}

	for _, tc := range tcs {
		t.Run(tc.name, func(t *testing.T) {
			ctx, cancel := context.WithCancel(ctx)
			t.Cleanup(cancel)

			req, err := http.NewRequestWithContext(ctx, "GET", s.URL+tc.path, nil)
			if err != nil {
				t.Fatalf("NewRequestWithContext: %v", err)
			}

			if tc.password != "" {
				req.SetBasicAuth("", tc.password)
			}
			if tc.headerToken != "" {
				req.Header.Set("Fup-Token", tc.headerToken)
			}

			resp, err := s.Client().Do(req)
			if err != nil {
				t.Fatalf("Do(%q): %v", s.URL+tc.path, err)
			}
			defer resp.Body.Close()

			if resp.StatusCode != tc.wantStatus {
				t.Errorf("StatusCode = %v; want %v", resp.StatusCode, tc.wantStatus)
			}

			body, err := io.ReadAll(resp.Body)
			if err != nil {
				t.Fatalf("ReadAll(Body): %v", err)
			}

			if tc.wantText != "" && string(body) != tc.wantText {
				t.Errorf("response body = %q; want %q", string(body), tc.wantText)
			}
		})
	}
}