package rest

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

	"github.com/gin-gonic/gin"
	"github.com/nexuscrm/backend/pkg/auth"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// ---------------------------------------------------------------------------
// SystemHandler.Versions — returns Salesforce API version list
// ---------------------------------------------------------------------------

func TestSystemHandler_Versions(t *testing.T) {
	handler := &SystemHandler{}
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("GET", "/services/data/", nil)

	handler.Versions(c)
	assert.Equal(t, http.StatusOK, w.Code)

	var versions []map[string]interface{}
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &versions))
	assert.NotEmpty(t, versions)

	// Check that v63.0 is present
	found := false
	for _, v := range versions {
		if v["version"] == "63.0" {
			found = true
			assert.Equal(t, "Spring '25", v["label"])
			assert.Equal(t, "/services/data/v63.0", v["url"])
		}
	}
	assert.True(t, found, "v63.0 should be in the versions list")
}

// ---------------------------------------------------------------------------
// SystemHandler.Limits — returns Salesforce limits
// ---------------------------------------------------------------------------

func TestSystemHandler_Limits(t *testing.T) {
	handler := &SystemHandler{}
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("GET", "/limits", nil)

	handler.Limits(c)
	assert.Equal(t, http.StatusOK, w.Code)

	var response map[string]interface{}
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &response))

	// Spot-check key limit entries
	requiredLimits := []string{
		"DailyApiRequests",
		"DailyBulkApiRequests",
		"DataStorageMB",
		"FileStorageMB",
		"SingleEmail",
		"StreamingApiConcurrentClients",
	}
	for _, key := range requiredLimits {
		assert.Contains(t, response, key, "Limits should contain %s", key)
		limitEntry, ok := response[key].(map[string]interface{})
		require.True(t, ok, "%s should be a map", key)
		assert.Contains(t, limitEntry, "Max")
		assert.Contains(t, limitEntry, "Remaining")
	}
}

// ---------------------------------------------------------------------------
// SystemHandler.ListResources — returns resource URLs
// ---------------------------------------------------------------------------

func TestSystemHandler_ListResources(t *testing.T) {
	handler := &SystemHandler{}
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("GET", "/services/data/v63.0/", nil)
	c.Params = gin.Params{{Key: "version", Value: "v63.0"}}

	authUser := auth.UserSession{
		ID:        "user-001",
		Name:      "Test",
		Email:     "test@example.com",
		ProfileId: constants.ProfileSystemAdmin,
	}
	c.Set(constants.ContextKeyUser, authUser)

	handler.ListResources(c)
	assert.Equal(t, http.StatusOK, w.Code)

	var response map[string]interface{}
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &response))

	// Spot-check required resource URLs
	requiredResources := []string{
		"sobjects", "search", "query", "queryAll", "connect",
		"tooling", "analytics", "composite", "limits", "metadata",
	}
	for _, key := range requiredResources {
		assert.Contains(t, response, key)
		url, ok := response[key].(string)
		require.True(t, ok)
		assert.Contains(t, url, "v63.0")
	}
}

func TestSystemHandler_ListResources_NoUser(t *testing.T) {
	handler := &SystemHandler{}
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("GET", "/services/data/v63.0/", nil)
	c.Params = gin.Params{{Key: "version", Value: "v63.0"}}

	// No user set — MustGetUser panics (requireAuth middleware not applied)
	assert.Panics(t, func() { handler.ListResources(c) })
	_ = w // not used — handler panics before writing response
}

// ---------------------------------------------------------------------------
// SystemHandler.GetIdentity — authenticated user
// ---------------------------------------------------------------------------

func TestSystemHandler_GetIdentity_AuthenticatedSelf(t *testing.T) {
	handler := &SystemHandler{}
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("GET", "/id/"+constants.DefaultOrgID+"/user-001", nil)
	c.Params = gin.Params{
		{Key: "orgId", Value: constants.DefaultOrgID},
		{Key: "userId", Value: "user-001"},
	}

	authUser := auth.UserSession{
		ID:        "user-001",
		Name:      "Test User",
		Email:     "test@example.com",
		ProfileId: constants.ProfileSystemAdmin,
	}
	c.Set(constants.ContextKeyUser, authUser)

	handler.GetIdentity(c)
	assert.Equal(t, http.StatusOK, w.Code)

	var response map[string]interface{}
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &response))
	assert.Equal(t, "user-001", response["user_id"])
	assert.Equal(t, constants.DefaultOrgID, response["organization_id"])
	assert.Equal(t, "test@example.com", response["username"])
	assert.Equal(t, true, response["active"])
}

func TestSystemHandler_GetIdentity_MeAlias(t *testing.T) {
	handler := &SystemHandler{}
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("GET", "/id/"+constants.DefaultOrgID+"/me", nil)
	c.Params = gin.Params{
		{Key: "orgId", Value: constants.DefaultOrgID},
		{Key: "userId", Value: "me"},
	}

	authUser := auth.UserSession{
		ID:        "user-abc",
		Name:      "Me User",
		Email:     "me@example.com",
		ProfileId: constants.ProfileStandardUser,
	}
	c.Set(constants.ContextKeyUser, authUser)

	handler.GetIdentity(c)
	assert.Equal(t, http.StatusOK, w.Code)

	var response map[string]interface{}
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &response))
	assert.Equal(t, "user-abc", response["user_id"])
}

func TestSystemHandler_GetIdentity_Unauthenticated(t *testing.T) {
	handler := &SystemHandler{}
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("GET", "/id/org/user", nil)
	c.Params = gin.Params{
		{Key: "orgId", Value: "org"},
		{Key: "userId", Value: "user"},
	}

	assert.Panics(t, func() { handler.GetIdentity(c) })
}

func TestSystemHandler_GetIdentity_ForbiddenOtherUser(t *testing.T) {
	handler := &SystemHandler{}
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("GET", "/id/org/other-user", nil)
	c.Params = gin.Params{
		{Key: "orgId", Value: constants.DefaultOrgID},
		{Key: "userId", Value: "other-user"},
	}

	// Standard user trying to view another user's identity
	authUser := auth.UserSession{
		ID:        "user-std",
		Name:      "Standard User",
		Email:     "std@example.com",
		ProfileId: constants.ProfileStandardUser,
	}
	c.Set(constants.ContextKeyUser, authUser)

	handler.GetIdentity(c)
	assert.Equal(t, http.StatusForbidden, w.Code)
}

func TestSystemHandler_GetIdentity_AdminCanViewOtherUser(t *testing.T) {
	handler := &SystemHandler{}
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("GET", "/id/org/other-user", nil)
	c.Params = gin.Params{
		{Key: "orgId", Value: constants.DefaultOrgID},
		{Key: "userId", Value: "other-user"},
	}

	// Admin user viewing another user's identity
	authUser := auth.UserSession{
		ID:        "admin-001",
		Name:      "Admin User",
		Email:     "admin@example.com",
		ProfileId: constants.ProfileSystemAdmin,
	}
	c.Set(constants.ContextKeyUser, authUser)

	handler.GetIdentity(c)
	assert.Equal(t, http.StatusOK, w.Code)

	var response map[string]interface{}
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &response))
	assert.Equal(t, "other-user", response["user_id"])
}

// ---------------------------------------------------------------------------
// helpers: getAPIBaseURL
// ---------------------------------------------------------------------------

func TestGetAPIBaseURL_Default(t *testing.T) {
	t.Setenv("API_BASE_URL", "")
	assert.Equal(t, "http://127.0.0.1:3001", getAPIBaseURL())
}

func TestGetAPIBaseURL_CustomEnv(t *testing.T) {
	t.Setenv("API_BASE_URL", "https://api.example.com")
	assert.Equal(t, "https://api.example.com", getAPIBaseURL())
}
