package dialectsfcrm

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

	"github.com/nexuscrm/dialect-sf-crm/wire"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/dialect"
	"github.com/nexuscrm/shared/pkg/models"
)

const defaultLocalAPIBaseURL = "http://127.0.0.1:3001"

// IdentityNeutralHandler handles GET /services/data/vXX.X/id/:orgId/:userId.
func IdentityNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	if req == nil || req.User == nil {
		return &dialect.Response{Status: http.StatusInternalServerError, Body: wire.SFErrorResponse(wire.SFCodeServerError, "authenticated user is required")}, nil
	}

	userIDParam := strings.TrimSpace(req.Params["userId"])
	targetUserID := req.User.ID
	if userIDParam != "" && userIDParam != "me" {
		if userIDParam != req.User.ID && !callerCanViewUserIdentity(ctx, deps.Contracts.Policy, req.User, userIDParam) {
			return &dialect.Response{Status: http.StatusForbidden, Body: wire.SFErrorResponse(wire.SFCodeForbidden, "Cannot view other user's identity")}, nil
		}
		targetUserID = userIDParam
	}

	baseURL := apiBaseURL()
	version := apiVersionOrDefault(req.Version)
	dataBaseURL := fmt.Sprintf("%s/services/data/%s", baseURL, version)
	soapVersion := apiVersionNumber(version)
	firstName, lastName := splitDisplayName(req.User.Name)

	response := map[string]any{
		"id":              fmt.Sprintf("%s/id/%s/%s", baseURL, constants.DefaultOrgID, targetUserID),
		"asserted_user":   true,
		"user_id":         targetUserID,
		"organization_id": constants.DefaultOrgID,
		"username":        req.User.Email,
		"nick_name":       req.User.Name,
		"display_name":    req.User.Name,
		"email":           req.User.Email,
		"email_verified":  true,
		"first_name":      firstName,
		"last_name":       lastName,
		"timezone":        "America/Los_Angeles",
		"photos": map[string]any{
			"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": map[string]any{
			"created_date": nil,
			"body":         nil,
		},
		"urls": map[string]any{
			"enterprise": fmt.Sprintf("%s/services/Soap/c/%s/%s", baseURL, soapVersion, constants.DefaultOrgID),
			"metadata":   fmt.Sprintf("%s/services/Soap/m/%s/%s", baseURL, soapVersion, constants.DefaultOrgID),
			"partner":    fmt.Sprintf("%s/services/Soap/u/%s/%s", baseURL, soapVersion, constants.DefaultOrgID),
			"rest":       fmt.Sprintf("%s/", dataBaseURL),
			"sobjects":   fmt.Sprintf("%s/sobjects/", dataBaseURL),
			"search":     fmt.Sprintf("%s/search/", dataBaseURL),
			"query":      fmt.Sprintf("%s/query/", dataBaseURL),
			"recent":     fmt.Sprintf("%s/recent/", dataBaseURL),
			"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",
	}

	return &dialect.Response{Status: http.StatusOK, Body: response}, nil
}

func callerCanViewUserIdentity(ctx context.Context, policy contract.Policy, user *models.UserSession, _ string) bool {
	capabilityPolicy, ok := policy.(contract.CapabilityPolicy)
	if !ok {
		return false
	}
	return capabilityPolicy.HasCapability(ctx, user, constants.PermViewAllUsers)
}

func apiBaseURL() string {
	if url := os.Getenv("API_BASE_URL"); url != "" {
		return url
	}
	return defaultLocalAPIBaseURL
}

func apiVersionNumber(version string) string {
	return strings.TrimPrefix(apiVersionOrDefault(version), "v")
}

func splitDisplayName(name string) (string, string) {
	firstName := name
	lastName := ""
	if parts := strings.SplitN(name, " ", 2); len(parts) == 2 {
		firstName = parts[0]
		lastName = parts[1]
	}
	return firstName, lastName
}
