package api

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

	"github.com/go-chi/chi/v5"

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

func newMeTimezoneRouter(h *handlers) chi.Router {
	r := chi.NewRouter()
	r.Put("/api/me/timezone", h.handlePutTimezone)
	return r
}

func TestPutTimezone_ValidIANA(t *testing.T) {
	if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" {
		t.Skip("MINSTREL_TEST_DATABASE_URL not set")
	}
	h, pool := testHandlers(t)
	user := seedUser(t, pool, "tz1", "irrelevant1", false)

	body := bytes.NewBufferString(`{"timezone":"America/New_York"}`)
	req := httptest.NewRequest(http.MethodPut, "/api/me/timezone", body)
	req.Header.Set("Content-Type", "application/json")
	req = withUser(req, user)
	rec := httptest.NewRecorder()
	newMeTimezoneRouter(h).ServeHTTP(rec, req)

	if rec.Code != http.StatusNoContent {
		t.Fatalf("status = %d, want 204; body=%s", rec.Code, rec.Body.String())
	}

	// Verify the DB row was updated.
	got, err := dbq.New(pool).GetUserByID(context.Background(), user.ID)
	if err != nil {
		t.Fatalf("GetUserByID: %v", err)
	}
	if got.Timezone != "America/New_York" {
		t.Errorf("timezone = %q, want %q", got.Timezone, "America/New_York")
	}
	if !got.TimezoneUpdatedAt.Valid {
		t.Errorf("expected timezone_updated_at to be set after PUT")
	}
}

func TestPutTimezone_InvalidIANA(t *testing.T) {
	if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" {
		t.Skip("MINSTREL_TEST_DATABASE_URL not set")
	}
	h, pool := testHandlers(t)
	user := seedUser(t, pool, "tz2", "irrelevant1", false)

	body := bytes.NewBufferString(`{"timezone":"Not/A/Real/Zone"}`)
	req := httptest.NewRequest(http.MethodPut, "/api/me/timezone", body)
	req.Header.Set("Content-Type", "application/json")
	req = withUser(req, user)
	rec := httptest.NewRecorder()
	newMeTimezoneRouter(h).ServeHTTP(rec, req)

	if rec.Code != http.StatusBadRequest {
		t.Fatalf("status = %d, want 400; body=%s", rec.Code, rec.Body.String())
	}

	// Error envelope is nested: {"error":{"code":...}} (matches the
	// rest of /api/*), not a top-level {"code":...}.
	var errResp struct {
		Error struct {
			Code string `json:"code"`
		} `json:"error"`
	}
	if err := json.NewDecoder(rec.Body).Decode(&errResp); err != nil {
		t.Fatalf("decode err response: %v", err)
	}
	if errResp.Error.Code != "invalid_timezone" {
		t.Errorf("error code = %q, want invalid_timezone", errResp.Error.Code)
	}
}

func TestPutTimezone_EmptyTimezone(t *testing.T) {
	if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" {
		t.Skip("MINSTREL_TEST_DATABASE_URL not set")
	}
	h, pool := testHandlers(t)
	user := seedUser(t, pool, "tz3", "irrelevant1", false)

	body := bytes.NewBufferString(`{"timezone":""}`)
	req := httptest.NewRequest(http.MethodPut, "/api/me/timezone", body)
	req.Header.Set("Content-Type", "application/json")
	req = withUser(req, user)
	rec := httptest.NewRecorder()
	newMeTimezoneRouter(h).ServeHTTP(rec, req)

	if rec.Code != http.StatusBadRequest {
		t.Fatalf("status = %d, want 400", rec.Code)
	}
}
