package webserver_test

import (
	"strings"
	"testing"

	"net/http/httptest"

	"github.com/andybalholm/cascadia"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"golang.org/x/net/html"

	. "gitlab.com/offline-twitter/twitter_offline_engine/pkg/persistence"
	"gitlab.com/offline-twitter/twitter_offline_engine/pkg/webserver"
)

func TestMessagesIndexPageRequiresActiveUser(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)

	// HTMX version
	req := httptest.NewRequest("GET", "/messages", nil)
	req.Header.Set("HX-Request", "true")
	resp := do_request(req) // No active user
	require.Equal(401, resp.StatusCode)
	// Piggyback in testing of HTMX 401 toasts
	assert.Equal("beforeend", resp.Header.Get("HX-Reswap"))
	assert.Equal("#toasts", resp.Header.Get("HX-Retarget"))
	assert.Equal("false", resp.Header.Get("HX-Push-Url"))

	// Non-HTMX version
	req1 := httptest.NewRequest("GET", "/messages", nil)
	resp1 := do_request(req1) // No active user
	require.Equal(401, resp1.StatusCode)
	assert.Equal("", resp1.Header.Get("HX-Reswap")) // HX-* stuff should be unset
}

// Loading the index page should work if you're logged in
func TestMessagesIndexPage(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)

	// Chat list
	resp := do_request_with_active_user(httptest.NewRequest("GET", "/messages", nil))
	require.Equal(200, resp.StatusCode)
	root, err := html.Parse(resp.Body)
	require.NoError(err)
	assert.Len(cascadia.QueryAll(root, selector(".chat-list .chat-list-entry")), 2)
	assert.Len(cascadia.QueryAll(root, selector(".chat-view .dm-message")), 0) // No messages until you click on one
}

// Users should only be able to open chats they're a member of
func TestMessagesRoomRequiresCorrectUser(t *testing.T) {
	require := require.New(t)

	// No active user
	resp := do_request(httptest.NewRequest("GET", "/messages/1488963321701171204-1178839081222115328", nil))
	require.Equal(401, resp.StatusCode)

	// Wrong user (not in the chat)
	// Copied from `do_request_with_active_user`
	recorder := httptest.NewRecorder()
	app := webserver.NewApp(profile)
	app.IsScrapingDisabled = true
	app.ActiveUser = User{ID: 782982734, Handle: "Not a real user"} // Simulate a login
	app.WithMiddlewares().ServeHTTP(recorder, httptest.NewRequest("GET", "/messages/1488963321701171204-1178839081222115328", nil))
	resp2 := recorder.Result()
	require.Equal(404, resp2.StatusCode)
}

// Open a chat room
func TestMessagesRoom(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)

	// Chat detail
	resp := do_request_with_active_user(httptest.NewRequest("GET", "/messages/1488963321701171204-1178839081222115328", nil))
	require.Equal(200, resp.StatusCode)
	root, err := html.Parse(resp.Body)
	require.NoError(err)
	assert.Len(cascadia.QueryAll(root, selector(".chat-list .chat-list-entry")), 2) // Chat list still renders
	assert.Len(cascadia.QueryAll(root, selector("#chat-view .dm-message")), 5)

	// Should have the poller at the bottom
	poller := cascadia.Query(root, selector("#new-messages-poller"))
	assert.NotNil(poller)
	assert.Contains(poller.Attr, html.Attribute{Key: "hx-get", Val: "/messages/1488963321701171204-1178839081222115328"})
	assert.Contains(
		cascadia.Query(poller, selector("input[name='scroll_bottom']")).Attr,
		html.Attribute{Key: "value", Val: "1"},
	)
	assert.Contains(
		cascadia.Query(poller, selector("input[name='latest_timestamp']")).Attr,
		html.Attribute{Key: "value", Val: "1686025129144"},
	)
}

// Loading the page since a given message
func TestMessagesRoomPollForUpdates(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)

	// Chat detail
	req := httptest.NewRequest("GET", "/messages/1488963321701171204-1178839081222115328?poll&latest_timestamp=1686025129141", nil)
	req.Header.Set("HX-Request", "true")
	resp := do_request_with_active_user(req)
	require.Equal(200, resp.StatusCode)
	root, err := html.Parse(resp.Body)
	require.NoError(err)
	assert.Len(cascadia.QueryAll(root, selector(".dm-message")), 3)

	// Should have the poller at the bottom
	poller := cascadia.Query(root, selector("#new-messages-poller"))
	assert.NotNil(poller)
	assert.Contains(poller.Attr, html.Attribute{Key: "hx-get", Val: "/messages/1488963321701171204-1178839081222115328"})
	assert.Contains(
		cascadia.Query(poller, selector("input[name='scroll_bottom']")).Attr,
		html.Attribute{Key: "value", Val: "1"},
	)
	assert.Contains(
		cascadia.Query(poller, selector("input[name='latest_timestamp']")).Attr,
		html.Attribute{Key: "value", Val: "1686025129144"},
	)
}

// Loading the page since latest message (no updates)
func TestMessagesRoomPollForUpdatesEmptyResult(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)

	// Chat detail
	req := httptest.NewRequest("GET", "/messages/1488963321701171204-1178839081222115328?poll&latest_timestamp=1686025129144", nil)
	req.Header.Set("HX-Request", "true")
	resp := do_request_with_active_user(req)
	require.Equal(200, resp.StatusCode)
	root, err := html.Parse(resp.Body)
	require.NoError(err)
	assert.Len(cascadia.QueryAll(root, selector(".dm-message")), 0)

	// Should have the poller at the bottom, with the same value as previously
	poller := cascadia.Query(root, selector("#new-messages-poller"))
	assert.NotNil(poller)
	assert.Contains(poller.Attr, html.Attribute{Key: "hx-get", Val: "/messages/1488963321701171204-1178839081222115328"})
	assert.Contains(
		cascadia.Query(poller, selector("input[name='scroll_bottom']")).Attr,
		html.Attribute{Key: "value", Val: "1"},
	)
	assert.Contains(
		cascadia.Query(poller, selector("input[name='latest_timestamp']")).Attr,
		html.Attribute{Key: "value", Val: "1686025129144"},
	)
}

// Scroll back in the messages
func TestMessagesPaginate(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)

	// Chat detail
	req := httptest.NewRequest("GET", "/messages/1488963321701171204-1178839081222115328?cursor=1686025129142", nil)
	req.Header.Set("HX-Request", "true")
	resp := do_request_with_active_user(req)
	require.Equal(200, resp.StatusCode)
	root, err := html.Parse(resp.Body)
	require.NoError(err)
	assert.Len(cascadia.QueryAll(root, selector(".dm-message")), 2)
}

// When scraping is disabled, marking as read should 401
func TestMessagesMarkAsRead(t *testing.T) {
	require := require.New(t)

	resp := do_request_with_active_user(httptest.NewRequest("GET", "/messages/1488963321701171204-1178839081222115328/mark-as-read", nil))
	require.Equal(resp.StatusCode, 401)
}

// When scraping is disabled, sending a message should 401
func TestMessagesSend(t *testing.T) {
	require := require.New(t)

	resp := do_request_with_active_user(httptest.NewRequest("GET",
		"/messages/1488963321701171204-1178839081222115328/send",
		strings.NewReader(`{"text": "bleh"}`),
	))
	require.Equal(401, resp.StatusCode)
}

// When scraping is disabled, sending a reacc should 401
func TestMessagesSendReacc(t *testing.T) {
	require := require.New(t)

	resp := do_request_with_active_user(httptest.NewRequest("GET",
		"/messages/1488963321701171204-1178839081222115328/reacc",
		strings.NewReader(`{"message_id": "1", "reacc": ":)"}`),
	))
	require.Equal(401, resp.StatusCode)
}

func TestMessagesRefreshConversationsList(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)

	// No active chat
	req := httptest.NewRequest("GET", "/messages/refresh-list", nil)
	req.Header.Set("HX-Request", "true")
	resp := do_request_with_active_user(req)
	require.Equal(200, resp.StatusCode)
	root, err := html.Parse(resp.Body)
	require.NoError(err)
	assert.Len(cascadia.QueryAll(root, selector(".chat-list-entry")), 2)
	assert.Len(cascadia.QueryAll(root, selector(".chat-list-entry.chat-list-entry--active-chat")), 0)

	// With an active chat
	req1 := httptest.NewRequest("GET", "/messages/refresh-list?active-chat=1488963321701171204-1178839081222115328", nil)
	req1.Header.Set("HX-Request", "true")
	resp1 := do_request_with_active_user(req1)
	require.Equal(200, resp1.StatusCode)
	root1, err := html.Parse(resp1.Body)
	require.NoError(err)
	assert.Len(cascadia.QueryAll(root1, selector(".chat-list-entry")), 2)
	assert.Len(cascadia.QueryAll(root1, selector(".chat-list-entry.chat-list-entry--active-chat")), 1)
}
