package rest

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/nexuscrm/backend/internal/application/services"
	"github.com/nexuscrm/backend/pkg/apierr"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/rs/zerolog/log"
)

// SystemHandler handles system-level endpoints (versions, limits, identity)
type SystemHandler struct {
	svcMgr *services.ServiceManager
}

func NewSystemHandler(svcMgr *services.ServiceManager) *SystemHandler {
	return &SystemHandler{svcMgr: svcMgr}
}

// ListResources handles GET /services/data/vXX.X/
func (h *SystemHandler) ListResources(c *gin.Context) {
	ver := c.Param("version")
	baseURL := fmt.Sprintf("/services/data/%s", ver)
	user := MustGetUser(c)
	userID := user.ID

	response := gin.H{
		"sobjects":            fmt.Sprintf("%s/sobjects", baseURL),
		"search":              fmt.Sprintf("%s/search", baseURL),
		"query":               fmt.Sprintf("%s/query", baseURL),
		"queryAll":            fmt.Sprintf("%s/queryAll", baseURL),
		"licensing":           fmt.Sprintf("%s/licensing", baseURL),
		"connect":             fmt.Sprintf("%s/connect", baseURL),
		"tooling":             fmt.Sprintf("%s/tooling", baseURL),
		"chatter":             fmt.Sprintf("%s/chatter", baseURL),
		"recent":              fmt.Sprintf("%s/recent", baseURL),
		"recyclebin":          fmt.Sprintf("%s/recyclebin", baseURL),
		"analytics":           fmt.Sprintf("%s/analytics", baseURL),
		"composite":           fmt.Sprintf("%s/composite", baseURL),
		"limits":              fmt.Sprintf("%s/limits", baseURL),
		"metadata":            fmt.Sprintf("%s/metadata", baseURL),
		"theme":               fmt.Sprintf("%s/theme", baseURL),
		"quickActions":        fmt.Sprintf("%s/quickActions", baseURL),
		"appMenu":             fmt.Sprintf("%s/appMenu", baseURL),
		"actions":             fmt.Sprintf("%s/actions", baseURL),
		"parameterizedSearch": fmt.Sprintf("%s/parameterizedSearch", baseURL),
		"process":             fmt.Sprintf("%s/process", baseURL),
		"event":               fmt.Sprintf("%s/event", baseURL),
		"jobs":                fmt.Sprintf("%s/jobs", baseURL),
		"ui-api":              fmt.Sprintf("%s/ui-api", baseURL),
		"identity":            fmt.Sprintf("%s/id/%s/%s", getAPIBaseURL(), constants.DefaultOrgID, userID),
	}
	c.JSON(http.StatusOK, response)
}

// Versions handles GET /services/data/
func (h *SystemHandler) Versions(c *gin.Context) {
	versions := []gin.H{
		{
			"label":   "Winter '24",
			"url":     "/services/data/v59.0",
			"version": "59.0",
		},
		{
			"label":   "Spring '24",
			"url":     "/services/data/v60.0",
			"version": "60.0",
		},
		{
			"label":   "Summer '24",
			"url":     "/services/data/v61.0",
			"version": "61.0",
		},
		{
			"label":   "Winter '25",
			"url":     "/services/data/v62.0",
			"version": "62.0",
		},
		{
			"label":   "Spring '25",
			"url":     "/services/data/v63.0",
			"version": "63.0",
		},
	}

	c.JSON(http.StatusOK, versions)
}

// Limits handles GET /limits
func (h *SystemHandler) Limits(c *gin.Context) {
	response := gin.H{
		// Connected App rate limit info (present only for Connected App sessions)
		// Values reflect the defaults enforced by AppRateLimiter middleware.
		// Phase 2 will source these from the ConnectedApp table per app.
	}

	if appID, exists := c.Get("connected_app_id"); exists {
		if appIDStr, ok := appID.(string); ok && appIDStr != "" {
			response["HourlyApiRequests"] = gin.H{
				"Max":       1000,
				"Remaining": 1000,
			}
			response["DailyApiRequests"] = gin.H{
				"Max":       10000,
				"Remaining": 10000,
			}
		}
	}

	// Standard org-level limits — added to response only if not already set
	// (Connected App block above may have set DailyApiRequests/HourlyApiRequests).
	standard := gin.H{
		"DailyApiRequests": gin.H{
			"Max":       100000,
			"Remaining": 99000,
		},
		"DailyAsyncApexExecutions": gin.H{
			"Max":       250000,
			"Remaining": 250000,
		},
		"DailyBulkApiRequests": gin.H{
			"Max":       10000,
			"Remaining": 10000,
		},
		"DailyBulkV2QueryJobs": gin.H{
			"Max":       1000,
			"Remaining": 1000,
		},
		"DailyStreamingApiEvents": gin.H{
			"Max":       200000,
			"Remaining": 200000,
		},
		"DataStorageMB": gin.H{
			"Max":       5000,
			"Remaining": 4900,
		},
		"FileStorageMB": gin.H{
			"Max":       10000,
			"Remaining": 9900,
		},
		"HourlyDashboardRefreshes": gin.H{
			"Max":       200,
			"Remaining": 200,
		},
		"HourlyDashboardResults": gin.H{
			"Max":       5000,
			"Remaining": 5000,
		},
		"HourlyDashboardStatuses": gin.H{
			"Max":       999999999,
			"Remaining": 999999999,
		},
		"HourlyODataCallout": gin.H{
			"Max":       10000,
			"Remaining": 10000,
		},
		"HourlySyncReportRuns": gin.H{
			"Max":       500,
			"Remaining": 500,
		},
		"HourlyTimeBasedWorkflow": gin.H{
			"Max":       1000,
			"Remaining": 1000,
		},
		"MassEmail": gin.H{
			"Max":       5000,
			"Remaining": 5000,
		},
		"SingleEmail": gin.H{
			"Max":       5000,
			"Remaining": 5000,
		},
		"StreamingApiConcurrentClients": gin.H{
			"Max":       2000,
			"Remaining": 2000,
		},
	}
	for k, v := range standard {
		if _, already := response[k]; !already {
			response[k] = v
		}
	}

	c.JSON(http.StatusOK, response)
}

// GetIdentity handles GET /id/:orgId/:userId
func (h *SystemHandler) GetIdentity(c *gin.Context) {
	userId := c.Param("userId")
	currentUser := MustGetUser(c)

	var targetUserID string
	if userId == "me" {
		targetUserID = currentUser.ID
	} else {
		if userId != currentUser.ID && !currentUser.IsSuperUser() {
			log.Warn().Str("token_user", currentUser.ID).Str("param_user", userId).Msg("Unauthorized identity access attempt")
			c.JSON(http.StatusForbidden, apierr.SFErrorResponse(apierr.SFCodeForbidden, "Cannot view other user's identity"))
			return
		}
		targetUserID = userId
	}

	baseURL := getAPIBaseURL()

	// Split display name into first/last for identity response
	firstName := currentUser.Name
	lastName := ""
	if parts := strings.SplitN(currentUser.Name, " ", 2); len(parts) == 2 {
		firstName = parts[0]
		lastName = parts[1]
	}

	response := gin.H{
		"id":              fmt.Sprintf("%s/id/%s/%s", baseURL, constants.DefaultOrgID, targetUserID),
		"asserted_user":   true,
		"user_id":         targetUserID,
		"organization_id": constants.DefaultOrgID,
		"username":        currentUser.Email,
		"nick_name":       currentUser.Name,
		"display_name":    currentUser.Name,
		"email":           currentUser.Email,
		"email_verified":  true,
		"first_name":      firstName,
		"last_name":       lastName,
		"timezone":        "America/Los_Angeles",
		"photos": gin.H{
			"picture":   fmt.Sprintf("%s/profilephoto/005/F", baseURL),
			"thumbnail": fmt.Sprintf("%s/profilephoto/005/T", baseURL),
		},
		"addr_street":           nil,
		"addr_city":             nil,
		"addr_state":            nil,
		"addr_country":          nil,
		"addr_zip":              nil,
		"mobile_phone":          nil,
		"mobile_phone_verified": false,
		"status": gin.H{
			"created_date": nil,
			"body":         nil,
		},
		"urls": gin.H{
			"enterprise": fmt.Sprintf("%s/services/Soap/c/63.0/%s", baseURL, constants.DefaultOrgID),
			"metadata":   fmt.Sprintf("%s/services/Soap/m/63.0/%s", baseURL, constants.DefaultOrgID),
			"partner":    fmt.Sprintf("%s/services/Soap/u/63.0/%s", baseURL, constants.DefaultOrgID),
			"rest":       fmt.Sprintf("%s/services/data/v63.0/", baseURL),
			"sobjects":   fmt.Sprintf("%s/services/data/v63.0/sobjects/", baseURL),
			"search":     fmt.Sprintf("%s/services/data/v63.0/search/", baseURL),
			"query":      fmt.Sprintf("%s/services/data/v63.0/query/", baseURL),
			"recent":     fmt.Sprintf("%s/services/data/v63.0/recent/", baseURL),
			"profile":    fmt.Sprintf("%s/005/%s", baseURL, targetUserID),
		},
		"active":             true,
		"user_type":          "STANDARD",
		"language":           "en_US",
		"locale":             "en_US",
		"utcOffset":          -28800000,
		"last_modified_date": "2024-01-01T00:00:00.000+0000",
	}

	c.JSON(http.StatusOK, response)
}
