package mail

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

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

	"github.com/ultisuite/ulti-backend/internal/api/middleware"
	"github.com/ultisuite/ulti-backend/internal/auth"
)

func newTestIdentityRouter(svc ServiceAPI) http.Handler {
	h := NewHandlerWithService(svc)
	r := chi.NewRouter()
	r.Use(middleware.WithTestClaims(&auth.Claims{
		Sub:   testExternalID,
		Email: "user@example.com",
	}))
	r.Get("/accounts/{accountID}/identities", h.ListIdentities)
	r.Post("/accounts/{accountID}/identities", h.CreateIdentity)
	r.Get("/identities/{identityID}", h.GetIdentity)
	r.Put("/identities/{identityID}", h.UpdateIdentity)
	r.Delete("/identities/{identityID}", h.DeleteIdentity)
	return r
}

func TestListIdentities(t *testing.T) {
	svc := newFakeMailService()
	router := newTestIdentityRouter(svc)

	req := httptest.NewRequest(http.MethodGet, "/accounts/"+testMailAccountID+"/identities", nil)
	rec := httptest.NewRecorder()
	router.ServeHTTP(rec, req)

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

	var body IdentitiesList
	if err := json.NewDecoder(rec.Body).Decode(&body); err != nil {
		t.Fatalf("decode body: %v", err)
	}
	if len(body.Identities) != 1 {
		t.Fatalf("identities len = %d, want 1", len(body.Identities))
	}
	if body.Identities[0]["email"] != "sender@example.com" {
		t.Fatalf("email = %v", body.Identities[0]["email"])
	}
}

func TestCreateIdentity(t *testing.T) {
	svc := newFakeMailService()
	router := newTestIdentityRouter(svc)

	payload := map[string]any{
		"email": "alias@example.com",
		"name":  "Alias",
	}
	body, err := json.Marshal(payload)
	if err != nil {
		t.Fatalf("marshal payload: %v", err)
	}

	req := httptest.NewRequest(http.MethodPost, "/accounts/"+testMailAccountID+"/identities", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	rec := httptest.NewRecorder()
	router.ServeHTTP(rec, req)

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

	var resp map[string]string
	if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil {
		t.Fatalf("decode body: %v", err)
	}
	if resp["id"] != "id-new" {
		t.Fatalf("id = %q, want id-new", resp["id"])
	}
}

func TestGetIdentity(t *testing.T) {
	svc := newFakeMailService()
	router := newTestIdentityRouter(svc)

	req := httptest.NewRequest(http.MethodGet, "/identities/id-1", nil)
	rec := httptest.NewRecorder()
	router.ServeHTTP(rec, req)

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

	var identity map[string]any
	if err := json.NewDecoder(rec.Body).Decode(&identity); err != nil {
		t.Fatalf("decode body: %v", err)
	}
	if identity["id"] != "id-1" {
		t.Fatalf("id = %v", identity["id"])
	}
}

func TestUpdateIdentity(t *testing.T) {
	svc := newFakeMailService()
	router := newTestIdentityRouter(svc)

	payload := map[string]any{
		"email":          "updated@example.com",
		"name":           "Updated",
		"is_default":     false,
		"signature_html": "<p>Sig</p>",
		"reply_to_addrs":   []string{"reply@example.com"},
	}
	body, err := json.Marshal(payload)
	if err != nil {
		t.Fatalf("marshal payload: %v", err)
	}

	req := httptest.NewRequest(http.MethodPut, "/identities/id-1", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	rec := httptest.NewRecorder()
	router.ServeHTTP(rec, req)

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

func TestDeleteIdentity(t *testing.T) {
	svc := newFakeMailService()
	router := newTestIdentityRouter(svc)

	req := httptest.NewRequest(http.MethodDelete, "/identities/id-1", nil)
	rec := httptest.NewRecorder()
	router.ServeHTTP(rec, req)

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

func TestListIdentitiesAccountNotFound(t *testing.T) {
	svc := newFakeMailService()
	router := newTestIdentityRouter(svc)

	req := httptest.NewRequest(http.MethodGet, "/accounts/missing/identities", nil)
	rec := httptest.NewRecorder()
	router.ServeHTTP(rec, req)

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