package handlers

import (
	"context"
	"bytes"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"

	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"golang.org/x/crypto/bcrypt"
)

func TestSignupLogsAuthStoreMutations(t *testing.T) {
	var buf bytes.Buffer
	restore := log.Logger
	log.Logger = zerolog.New(&buf)
	defer func() {
		log.Logger = restore
	}()

	handler := newTestAuthHandler(t)

	form := url.Values{}
	form.Set("email", "new@xtablo.com")
	form.Set("password", "xtablo-secret")

	req := httptest.NewRequest(http.MethodPost, "/signup", strings.NewReader(form.Encode()))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	rec := httptest.NewRecorder()

	handler.PostSignup().ServeHTTP(rec, req)

	output := buf.String()
	for _, want := range []string{
		`"action":"create_user"`,
		`"email":"new@xtablo.com"`,
		`"action":"create_session"`,
		`"session_id":"`,
	} {
		if !strings.Contains(output, want) {
			t.Fatalf("expected log output to contain %q, got %q", want, output)
		}
	}
}

func TestSignupHashesPasswordBeforeStoringUser(t *testing.T) {
	repo := NewInMemoryAuthRepository()
	handler := NewAuthHandler(repo)

	form := url.Values{}
	form.Set("email", "new@xtablo.com")
	form.Set("password", "xtablo-secret")

	req := httptest.NewRequest(http.MethodPost, "/signup", strings.NewReader(form.Encode()))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	rec := httptest.NewRecorder()

	handler.PostSignup().ServeHTTP(rec, req)

	if rec.Code != http.StatusOK {
		t.Fatalf("expected status 200, got %d", rec.Code)
	}

	storedUser, err := repo.GetAuthUserByEmail(req.Context(), "new@xtablo.com")
	if err != nil {
		t.Fatalf("expected stored user, got error %v", err)
	}
	if storedUser.EncryptedPassword == "xtablo-secret" {
		t.Fatalf("expected stored password hash, got plaintext")
	}
	if bcrypt.CompareHashAndPassword([]byte(storedUser.EncryptedPassword), []byte("xtablo-secret")) != nil {
		t.Fatalf("expected stored password to match bcrypt hash")
	}
}

func TestLogoutLogsSessionDeletion(t *testing.T) {
	var buf bytes.Buffer
	restore := log.Logger
	log.Logger = zerolog.New(&buf)
	defer func() {
		log.Logger = restore
	}()

	handler := newTestAuthHandler(t)

	loginForm := url.Values{}
	loginForm.Set("email", "demo@xtablo.com")
	loginForm.Set("password", "xtablo-demo")

	loginReq := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(loginForm.Encode()))
	loginReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	loginRec := httptest.NewRecorder()
	handler.PostLogin().ServeHTTP(loginRec, loginReq)

	sessionCookie := loginRec.Result().Cookies()[0]

	logoutReq := httptest.NewRequest(http.MethodPost, "/logout", nil)
	logoutReq.AddCookie(sessionCookie)
	logoutRec := httptest.NewRecorder()
	handler.PostLogout().ServeHTTP(logoutRec, logoutReq)

	output := buf.String()
	for _, want := range []string{
		`"action":"delete_session"`,
		`"email":"demo@xtablo.com"`,
		`"session_id":"`,
	} {
		if !strings.Contains(output, want) {
			t.Fatalf("expected log output to contain %q, got %q", want, output)
		}
	}
}

func TestTasksPageSidebarShowsRealTablos(t *testing.T) {
	repo := NewInMemoryAuthRepository()
	handler := NewAuthHandler(repo)
	sessionCookie := loginTestUser(t, handler, "demo@xtablo.com", "xtablo-demo")

	req := httptest.NewRequest(http.MethodGet, "/", nil)
	req.AddCookie(sessionCookie)
	userID, ok := handler.currentUserID(req.Context(), req)
	if !ok {
		t.Fatal("expected user session")
	}

	for _, tablo := range []CreateTabloInput{
		{OwnerID: userID, Name: "Blue", Color: "#3B82F6", Status: TabloStatusTodo},
		{OwnerID: userID, Name: "Green", Color: "#22C55E", Status: TabloStatusTodo},
		{OwnerID: userID, Name: "Purple", Color: "#A855F7", Status: TabloStatusTodo},
		{OwnerID: userID, Name: "Red", Color: "#EF4444", Status: TabloStatusTodo},
		{OwnerID: userID, Name: "Hidden Fifth", Color: "#EAB308", Status: TabloStatusTodo},
	} {
		if _, err := repo.CreateTablo(context.Background(), tablo); err != nil {
			t.Fatalf("create tablo %q: %v", tablo.Name, err)
		}
	}

	pageReq := httptest.NewRequest(http.MethodGet, "/tasks", nil)
	pageReq.AddCookie(sessionCookie)
	rec := httptest.NewRecorder()

	handler.GetTasksPage().ServeHTTP(rec, pageReq)

	if rec.Code != http.StatusOK {
		t.Fatalf("expected status 200, got %d", rec.Code)
	}

	body := rec.Body.String()
	projectSection := body
	if index := strings.Index(body, `id="sidebar-projects-section"`); index >= 0 {
		projectSection = body[index:]
		if end := strings.Index(projectSection, `<ul class="sidebar-list sidebar-footer-links"`); end >= 0 {
			projectSection = projectSection[:end]
		}
	}
	for _, want := range []string{
		"Green",
		"Purple",
		"Red",
		"Hidden Fifth",
	} {
		if !strings.Contains(projectSection, want) {
			t.Fatalf("expected sidebar to contain %q, got %q", want, projectSection)
		}
	}
	if strings.Contains(projectSection, "Blue") {
		t.Fatalf("expected sidebar to limit to the four most recent tablos, got %q", projectSection)
	}
	if !strings.Contains(body, `href="/tablos/`) {
		t.Fatalf("expected sidebar project links, got %q", body)
	}
}

func newTestAuthHandler(t *testing.T) *AuthHandler {
	t.Helper()
	return NewAuthHandler(NewInMemoryAuthRepository())
}
