package api

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

	"git.fabledsword.com/bvandeusen/minstrel/internal/auth"
	"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
)

type lbStatusJSON struct {
	Enabled         bool    `json:"enabled"`
	TokenSet        bool    `json:"token_set"`
	LastScrobbledAt *string `json:"last_scrobbled_at"`
}

func callGetLB(h *handlers, user dbq.User) *httptest.ResponseRecorder {
	req := httptest.NewRequest(http.MethodGet, "/api/me/listenbrainz", nil)
	req = req.WithContext(context.WithValue(req.Context(), auth.UserCtxKeyForTest(), user))
	w := httptest.NewRecorder()
	h.handleGetListenBrainz(w, req)
	return w
}

func callPutLB(h *handlers, user dbq.User, body any) *httptest.ResponseRecorder {
	buf, _ := json.Marshal(body)
	req := httptest.NewRequest(http.MethodPut, "/api/me/listenbrainz", bytes.NewReader(buf))
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(context.WithValue(req.Context(), auth.UserCtxKeyForTest(), user))
	w := httptest.NewRecorder()
	h.handlePutListenBrainz(w, req)
	return w
}

func TestHandleGetListenBrainz_NoTokenInitial(t *testing.T) {
	h, pool := testHandlers(t)
	truncateLibrary(t, pool)
	u := seedUser(t, pool, "alice", "x", false)
	w := callGetLB(h, u)
	if w.Code != http.StatusOK {
		t.Fatalf("status = %d body = %s", w.Code, w.Body.String())
	}
	var resp lbStatusJSON
	_ = json.Unmarshal(w.Body.Bytes(), &resp)
	if resp.Enabled || resp.TokenSet || resp.LastScrobbledAt != nil {
		t.Errorf("initial state wrong: %+v", resp)
	}
}

func TestHandlePutListenBrainz_SetTokenThenGet(t *testing.T) {
	h, pool := testHandlers(t)
	truncateLibrary(t, pool)
	u := seedUser(t, pool, "alice", "x", false)

	put := callPutLB(h, u, map[string]any{"token": "abc"})
	if put.Code != http.StatusOK {
		t.Fatalf("PUT status = %d body = %s", put.Code, put.Body.String())
	}

	get := callGetLB(h, u)
	var resp lbStatusJSON
	_ = json.Unmarshal(get.Body.Bytes(), &resp)
	if !resp.TokenSet {
		t.Error("TokenSet = false after PUT, want true")
	}
}

func TestHandlePutListenBrainz_ClearTokenForcesEnabledFalse(t *testing.T) {
	h, pool := testHandlers(t)
	truncateLibrary(t, pool)
	u := seedUser(t, pool, "alice", "x", false)

	_ = callPutLB(h, u, map[string]any{"token": "abc"})
	_ = callPutLB(h, u, map[string]any{"enabled": true})
	clear := callPutLB(h, u, map[string]any{"token": ""})
	if clear.Code != http.StatusOK {
		t.Fatalf("clear status = %d body = %s", clear.Code, clear.Body.String())
	}

	get := callGetLB(h, u)
	var resp lbStatusJSON
	_ = json.Unmarshal(get.Body.Bytes(), &resp)
	if resp.TokenSet {
		t.Error("TokenSet = true after clearing")
	}
	if resp.Enabled {
		t.Error("Enabled = true after clearing token, want false (force-cleared)")
	}
}

func TestHandlePutListenBrainz_EnableWithoutToken_400(t *testing.T) {
	h, pool := testHandlers(t)
	truncateLibrary(t, pool)
	u := seedUser(t, pool, "alice", "x", false)
	resp := callPutLB(h, u, map[string]any{"enabled": true})
	if resp.Code != http.StatusBadRequest {
		t.Errorf("status = %d, want 400 (cannot enable without token)", resp.Code)
	}
}
