package service

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestAuthFlowRegisterLoginAndMe(t *testing.T) {
	previous := backendStore
	backendStore = newTestGORMStore(t)
	defer func() { backendStore = previous }()

	handler := NewHandler(Config{Name: "Auth Service", Domain: "auth"})

	register := performJSONRequest(t, handler, http.MethodPost, "/api/auth/register", map[string]string{
		"login":    "qa_user",
		"password": "verysecret",
		"name":     "QA User",
	}, "")
	if register.Code != http.StatusCreated {
		t.Fatalf("register status = %d body=%s", register.Code, register.Body.String())
	}

	login := performJSONRequest(t, handler, http.MethodPost, "/api/auth/login", map[string]string{
		"login":    "qa_user",
		"password": "verysecret",
	}, "")
	if login.Code != http.StatusOK {
		t.Fatalf("login status = %d body=%s", login.Code, login.Body.String())
	}

	token := readTokenFromResponse(t, login.Body.Bytes())
	me := performJSONRequest(t, handler, http.MethodGet, "/api/auth/me", nil, token)
	if me.Code != http.StatusOK {
		t.Fatalf("me status = %d body=%s", me.Code, me.Body.String())
	}

	var payload map[string]map[string]any
	if err := json.Unmarshal(me.Body.Bytes(), &payload); err != nil {
		t.Fatalf("decode me response: %v", err)
	}
	if payload["user"]["login"] != "qa_user" {
		t.Fatalf("expected qa_user, got %#v", payload["user"]["login"])
	}
}

func TestAuthChangePassword(t *testing.T) {
	previous := backendStore
	backendStore = newTestGORMStore(t)
	defer func() { backendStore = previous }()

	handler := NewHandler(Config{Name: "Auth Service", Domain: "auth"})

	login := performJSONRequest(t, handler, http.MethodPost, "/api/auth/login", map[string]string{
		"login":    "demo_admin",
		"password": "demo_password",
	}, "")
	if login.Code != http.StatusOK {
		t.Fatalf("initial login status = %d body=%s", login.Code, login.Body.String())
	}
	token := readTokenFromResponse(t, login.Body.Bytes())

	change := performJSONRequest(t, handler, http.MethodPost, "/api/auth/change-password", map[string]string{
		"nextPassword": "demo_password_new",
	}, token)
	if change.Code != http.StatusOK {
		t.Fatalf("change password status = %d body=%s", change.Code, change.Body.String())
	}

	oldLogin := performJSONRequest(t, handler, http.MethodPost, "/api/auth/login", map[string]string{
		"login":    "demo_admin",
		"password": "demo_password",
	}, "")
	if oldLogin.Code != http.StatusUnauthorized {
		t.Fatalf("old password login status = %d body=%s", oldLogin.Code, oldLogin.Body.String())
	}

	newLogin := performJSONRequest(t, handler, http.MethodPost, "/api/auth/login", map[string]string{
		"login":    "demo_admin",
		"password": "demo_password_new",
	}, "")
	if newLogin.Code != http.StatusOK {
		t.Fatalf("new password login status = %d body=%s", newLogin.Code, newLogin.Body.String())
	}
}

func performJSONRequest(t *testing.T, handler http.Handler, method, path string, body any, token string) *httptest.ResponseRecorder {
	t.Helper()

	var reader *bytes.Reader
	if body == nil {
		reader = bytes.NewReader(nil)
	} else {
		payload, err := json.Marshal(body)
		if err != nil {
			t.Fatalf("marshal request: %v", err)
		}
		reader = bytes.NewReader(payload)
	}

	recorder := httptest.NewRecorder()
	request := httptest.NewRequest(method, path, reader)
	if body != nil {
		request.Header.Set("Content-Type", "application/json")
	}
	if token != "" {
		request.Header.Set("Authorization", "Bearer "+token)
	}
	handler.ServeHTTP(recorder, request)
	return recorder
}

func readTokenFromResponse(t *testing.T, body []byte) string {
	t.Helper()
	var payload map[string]any
	if err := json.Unmarshal(body, &payload); err != nil {
		t.Fatalf("decode token response: %v", err)
	}
	token, _ := payload["token"].(string)
	if token == "" {
		t.Fatalf("expected token in response: %s", string(body))
	}
	return token
}
