package api

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/gin-gonic/gin"

	"account/internal/auth"
	"account/internal/store"
)

type xworkmateAccessContext struct {
	Tenant              *store.Tenant
	Domain              string
	MembershipRole      string
	ProfileScope        string
	CanEditIntegrations bool
	CanManageTenant     bool
}

type xworkmateProfilePayload struct {
	OpenclawURL     string                          `json:"openclawUrl"`
	OpenclawOrigin  string                          `json:"openclawOrigin"`
	VaultURL        string                          `json:"vaultUrl"`
	VaultNamespace  string                          `json:"vaultNamespace"`
	VaultSecretPath string                          `json:"vaultSecretPath"`
	VaultSecretKey  string                          `json:"vaultSecretKey"`
	SecretLocators  []xworkmateSecretLocatorPayload `json:"secretLocators"`
	ApisixURL       string                          `json:"apisixUrl"`
}

type xworkmateSecretLocatorPayload struct {
	ID         string `json:"id"`
	Provider   string `json:"provider"`
	SecretPath string `json:"secretPath"`
	SecretKey  string `json:"secretKey"`
	Target     string `json:"target"`
	Required   bool   `json:"required"`
}

var xworkmateForbiddenTokenFields = map[string]struct{}{
	"openclawtoken": {},
	"gatewaytoken":  {},
	"vaulttoken":    {},
	"apisixtoken":   {},
}

func (h *handler) ensureSharedXWorkmateTenant(ctx context.Context) error {
	tenant := &store.Tenant{
		ID:      store.SharedXWorkmateTenantID,
		Name:    store.SharedXWorkmateTenantName,
		Edition: store.SharedPublicTenantEdition,
	}
	if err := h.store.EnsureTenant(ctx, tenant); err != nil {
		return err
	}

	return h.store.EnsureTenantDomain(ctx, &store.TenantDomain{
		TenantID:  tenant.ID,
		Domain:    store.SharedXWorkmateDomain,
		Kind:      store.TenantDomainKindGenerated,
		IsPrimary: true,
		Status:    store.TenantDomainStatusVerified,
	})
}

func (h *handler) resolveTenantHost(c *gin.Context) string {
	for _, headerName := range []string{"X-Forwarded-Host", "X-Original-Host", "X-Host"} {
		if candidate := store.NormalizeHostname(c.GetHeader(headerName)); candidate != "" {
			return candidate
		}
	}
	if candidate := store.NormalizeHostname(c.Request.Host); candidate != "" {
		return candidate
	}
	return store.SharedXWorkmateDomain
}

func (h *handler) resolveFrontendURL(c *gin.Context) string {
	candidates := []string{
		strings.TrimSpace(c.Query("frontend_url")),
		strings.TrimSpace(c.GetHeader("X-Frontend-Url")),
		strings.TrimSpace(c.GetHeader("Origin")),
	}
	if referer := strings.TrimSpace(c.GetHeader("Referer")); referer != "" {
		if parsed, err := url.Parse(referer); err == nil && parsed.Host != "" {
			candidates = append(candidates, fmt.Sprintf("%s://%s", parsed.Scheme, parsed.Host))
		}
	}
	candidates = append(candidates, strings.TrimSpace(h.oauthFrontendURL))

	for _, candidate := range candidates {
		if validated := h.validateFrontendURL(candidate); validated != "" {
			return validated
		}
	}
	return ""
}

func (h *handler) validateFrontendURL(raw string) string {
	trimmed := strings.TrimSpace(raw)
	if trimmed == "" {
		return ""
	}

	parsed, err := url.Parse(trimmed)
	if err != nil || parsed.Host == "" {
		return ""
	}
	if parsed.Scheme != "http" && parsed.Scheme != "https" {
		return ""
	}

	host := store.NormalizeHostname(parsed.Host)
	if host == "" {
		return ""
	}
	if store.IsSharedTenantHost(host) {
		return fmt.Sprintf("%s://%s", parsed.Scheme, parsed.Host)
	}
	if _, _, err := h.store.ResolveTenantByHost(context.Background(), host); err == nil {
		return fmt.Sprintf("%s://%s", parsed.Scheme, parsed.Host)
	}
	return ""
}

func buildOAuthState(frontendURL string) string {
	nonce := generateRandomState()
	trimmed := strings.TrimSpace(frontendURL)
	if trimmed == "" {
		return nonce
	}
	encoded := base64.RawURLEncoding.EncodeToString([]byte(trimmed))
	return nonce + "." + encoded
}

func parseOAuthStateFrontendURL(state string) string {
	parts := strings.SplitN(strings.TrimSpace(state), ".", 2)
	if len(parts) != 2 {
		return ""
	}
	decoded, err := base64.RawURLEncoding.DecodeString(parts[1])
	if err != nil {
		return ""
	}
	return strings.TrimSpace(string(decoded))
}

func generateRandomState() string {
	return (&handler{}).generateState()
}

func (h *handler) currentAuthenticatedUser(c *gin.Context) (*store.User, bool) {
	userID := strings.TrimSpace(auth.GetUserID(c))
	if userID == "" || userID == "system" {
		respondError(c, http.StatusUnauthorized, "session_token_required", "session token is required")
		return nil, false
	}

	user, err := h.store.GetUserByID(c.Request.Context(), userID)
	if err != nil {
		respondError(c, http.StatusUnauthorized, "session_user_lookup_failed", "failed to load session user")
		return nil, false
	}
	if !user.Active {
		respondError(c, http.StatusForbidden, "account_suspended", "your account has been suspended")
		return nil, false
	}
	return user, true
}

func (h *handler) ensureSharedTenantMembership(ctx context.Context, user *store.User) (string, error) {
	role := store.TenantMembershipRoleUser
	if h.isRootAccount(user) ||
		strings.EqualFold(strings.TrimSpace(user.Role), store.RoleAdmin) ||
		strings.EqualFold(strings.TrimSpace(user.Role), store.RoleOperator) {
		role = store.TenantMembershipRoleAdmin
	}
	return role, h.store.UpsertTenantMembership(ctx, &store.TenantMembership{
		TenantID: store.SharedXWorkmateTenantID,
		UserID:   user.ID,
		Role:     role,
	})
}

func (h *handler) resolveXWorkmateAccess(ctx context.Context, host string, user *store.User) (*xworkmateAccessContext, error) {
	normalizedHost := store.NormalizeHostname(host)
	if store.IsSharedTenantHost(normalizedHost) {
		if err := h.ensureSharedXWorkmateTenant(ctx); err != nil {
			return nil, err
		}
	}

	tenant, domain, err := h.store.ResolveTenantByHost(ctx, normalizedHost)
	if err != nil {
		return nil, err
	}

	access := &xworkmateAccessContext{
		Tenant: tenant,
		Domain: store.SharedXWorkmateDomain,
	}
	if domain != nil && strings.TrimSpace(domain.Domain) != "" {
		access.Domain = domain.Domain
	}

	if tenant.Edition == store.SharedPublicTenantEdition {
		role, err := h.ensureSharedTenantMembership(ctx, user)
		if err != nil {
			return nil, err
		}
		access.MembershipRole = role
		access.ProfileScope = store.XWorkmateProfileScopeTenantShared
		access.CanEditIntegrations = role == store.TenantMembershipRoleAdmin
		access.CanManageTenant = access.CanEditIntegrations
		return access, nil
	}

	membership, err := h.store.GetTenantMembership(ctx, tenant.ID, user.ID)
	if err != nil {
		return nil, err
	}

	access.MembershipRole = membership.Role
	access.ProfileScope = store.XWorkmateProfileScopeUserPrivate
	access.CanEditIntegrations = true
	access.CanManageTenant = membership.Role == store.TenantMembershipRoleAdmin
	return access, nil
}

func buildSessionTenantEntries(memberships []store.TenantMembership) []gin.H {
	if len(memberships) == 0 {
		return []gin.H{}
	}

	result := make([]gin.H, 0, len(memberships))
	for _, membership := range memberships {
		entry := gin.H{
			"id":   membership.TenantID,
			"role": membership.Role,
		}
		if strings.TrimSpace(membership.TenantName) != "" {
			entry["name"] = membership.TenantName
		}
		result = append(result, entry)
	}
	return result
}

func buildXWorkmateTokenConfigured(profile *store.XWorkmateProfile) gin.H {
	result := gin.H{
		"openclaw": false,
		"vault":    false,
		"apisix":   false,
	}
	if profile == nil {
		return result
	}

	if hasOpenclawXWorkmateSecretLocator(profile) {
		result["openclaw"] = true
	}

	return result
}

func buildXWorkmateTokenConfiguredWithVaultStatus(profile *store.XWorkmateProfile, vaultStatus map[string]bool) gin.H {
	result := buildXWorkmateTokenConfigured(profile)
	if len(vaultStatus) == 0 {
		return result
	}

	if configured, ok := vaultStatus[store.XWorkmateSecretLocatorTargetOpenclawGatewayToken]; ok {
		result["openclaw"] = configured
	}
	if configured, ok := vaultStatus[store.XWorkmateSecretLocatorTargetVaultRootToken]; ok {
		result["vault"] = configured
	}
	if configured, ok := vaultStatus[store.XWorkmateSecretLocatorTargetAIGatewayAccessToken]; ok {
		result["apisix"] = configured
	}

	return result
}

func hasOpenclawXWorkmateSecretLocator(profile *store.XWorkmateProfile) bool {
	if profile == nil {
		return false
	}

	if strings.TrimSpace(profile.VaultSecretPath) != "" && strings.TrimSpace(profile.VaultSecretKey) != "" {
		return true
	}
	for _, locator := range profile.SecretLocators {
		if locator.Target != store.XWorkmateSecretLocatorTargetOpenclawGatewayToken {
			continue
		}
		if strings.TrimSpace(locator.SecretPath) != "" && strings.TrimSpace(locator.SecretKey) != "" {
			return true
		}
	}
	return false
}

func buildXWorkmateSecretLocators(profile *store.XWorkmateProfile) []gin.H {
	if profile == nil || len(profile.SecretLocators) == 0 {
		return []gin.H{}
	}

	result := make([]gin.H, 0, len(profile.SecretLocators))
	for _, locator := range profile.SecretLocators {
		entry := gin.H{
			"id":         locator.ID,
			"provider":   locator.Provider,
			"secretPath": locator.SecretPath,
			"secretKey":  locator.SecretKey,
			"target":     locator.Target,
			"required":   locator.Required,
		}
		result = append(result, entry)
	}
	return result
}

func buildStoreXWorkmateSecretLocators(locators []xworkmateSecretLocatorPayload) []store.XWorkmateSecretLocator {
	if len(locators) == 0 {
		return []store.XWorkmateSecretLocator{}
	}

	result := make([]store.XWorkmateSecretLocator, 0, len(locators))
	for _, locator := range locators {
		result = append(result, store.XWorkmateSecretLocator{
			ID:         locator.ID,
			Provider:   locator.Provider,
			SecretPath: locator.SecretPath,
			SecretKey:  locator.SecretKey,
			Target:     locator.Target,
			Required:   locator.Required,
		})
	}
	return result
}

func (h *handler) buildSessionUser(ctx context.Context, host string, user *store.User) (gin.H, error) {
	access, err := h.resolveXWorkmateAccess(ctx, host, user)
	if err != nil {
		return nil, err
	}

	memberships, err := h.store.ListTenantMembershipsByUser(ctx, user.ID)
	if err != nil {
		return nil, err
	}

	payload := sanitizeUser(user, nil)
	payload["tenantId"] = access.Tenant.ID
	payload["tenants"] = buildSessionTenantEntries(memberships)
	return payload, nil
}

func buildXWorkmateProfileResponse(access *xworkmateAccessContext, profile *store.XWorkmateProfile, tokenConfigured gin.H) gin.H {
	resolvedProfile := gin.H{
		"openclawUrl":     "",
		"openclawOrigin":  "",
		"vaultUrl":        "",
		"vaultNamespace":  "",
		"vaultSecretPath": "",
		"vaultSecretKey":  "",
		"secretLocators":  []gin.H{},
		"apisixUrl":       "",
	}
	if profile != nil {
		resolvedProfile["openclawUrl"] = profile.OpenclawURL
		resolvedProfile["openclawOrigin"] = profile.OpenclawOrigin
		resolvedProfile["vaultUrl"] = profile.VaultURL
		resolvedProfile["vaultNamespace"] = profile.VaultNamespace
		resolvedProfile["vaultSecretPath"] = profile.VaultSecretPath
		resolvedProfile["vaultSecretKey"] = profile.VaultSecretKey
		resolvedProfile["secretLocators"] = buildXWorkmateSecretLocators(profile)
		resolvedProfile["apisixUrl"] = profile.ApisixURL
	}

	return gin.H{
		"edition": access.Tenant.Edition,
		"tenant": gin.H{
			"id":     access.Tenant.ID,
			"name":   access.Tenant.Name,
			"domain": access.Domain,
		},
		"membershipRole":      access.MembershipRole,
		"profileScope":        access.ProfileScope,
		"canEditIntegrations": access.CanEditIntegrations,
		"canManageTenant":     access.CanManageTenant,
		"profile":             resolvedProfile,
		"tokenConfigured":     tokenConfigured,
	}
}

func resolvedXWorkmateProfileUserID(access *xworkmateAccessContext, user *store.User) string {
	if access == nil {
		return ""
	}
	if access.ProfileScope == store.XWorkmateProfileScopeTenantShared {
		return ""
	}
	if user == nil {
		return ""
	}
	return strings.TrimSpace(user.ID)
}

func (h *handler) loadXWorkmateProfile(ctx context.Context, access *xworkmateAccessContext, user *store.User) (*store.XWorkmateProfile, error) {
	profileUserID := resolvedXWorkmateProfileUserID(access, user)
	profile, err := h.store.GetXWorkmateProfile(ctx, access.Tenant.ID, profileUserID, access.ProfileScope)
	if err == nil {
		return profile, nil
	}
	if !errors.Is(err, store.ErrXWorkmateProfileNotFound) {
		return nil, err
	}
	if access.ProfileScope != store.XWorkmateProfileScopeTenantShared {
		return nil, nil
	}

	legacyProfile, legacyErr := h.store.GetXWorkmateProfile(ctx, access.Tenant.ID, strings.TrimSpace(user.ID), access.ProfileScope)
	if legacyErr != nil {
		if errors.Is(legacyErr, store.ErrXWorkmateProfileNotFound) {
			return nil, nil
		}
		return nil, legacyErr
	}
	return legacyProfile, nil
}

func (h *handler) ensureXWorkmateVaultService(c *gin.Context) bool {
	if h.xworkmateVaultService != nil {
		return true
	}
	respondError(c, http.StatusServiceUnavailable, "xworkmate_vault_unavailable", "xworkmate vault integration is not configured")
	return false
}

func findStoredXWorkmateSecretLocator(profile *store.XWorkmateProfile, target string) (store.XWorkmateSecretLocator, bool) {
	if profile == nil {
		return store.XWorkmateSecretLocator{}, false
	}
	normalizedTarget := strings.ToLower(strings.TrimSpace(target))
	for _, locator := range profile.SecretLocators {
		if locator.Target != normalizedTarget {
			continue
		}
		if strings.TrimSpace(locator.SecretPath) == "" || strings.TrimSpace(locator.SecretKey) == "" {
			continue
		}
		store.NormalizeXWorkmateSecretLocator(&locator)
		return locator, true
	}
	return store.XWorkmateSecretLocator{}, false
}

func upsertXWorkmateSecretLocator(profile *store.XWorkmateProfile, locator store.XWorkmateSecretLocator) {
	if profile == nil {
		return
	}
	store.NormalizeXWorkmateSecretLocator(&locator)
	for i := range profile.SecretLocators {
		if profile.SecretLocators[i].Target != locator.Target {
			continue
		}
		profile.SecretLocators[i].ID = locator.ID
		profile.SecretLocators[i].Provider = locator.Provider
		profile.SecretLocators[i].SecretPath = locator.SecretPath
		profile.SecretLocators[i].SecretKey = locator.SecretKey
		profile.SecretLocators[i].Required = locator.Required
		store.NormalizeXWorkmateProfile(profile)
		return
	}
	profile.SecretLocators = append(profile.SecretLocators, locator)
	store.NormalizeXWorkmateProfile(profile)
}

func buildXWorkmateSecretStatusPayload(locator store.XWorkmateSecretLocator, configured bool) gin.H {
	managedTarget, _ := findXWorkmateManagedTarget(locator.Target)
	return gin.H{
		"target":     locator.Target,
		"configured": configured,
		"state": func() string {
			if configured {
				return "configured"
			}
			return "missing"
		}(),
		"required": managedTarget.Required || locator.Required,
		"locator": gin.H{
			"id":         locator.ID,
			"provider":   locator.Provider,
			"secretPath": locator.SecretPath,
			"secretKey":  locator.SecretKey,
			"target":     locator.Target,
			"required":   managedTarget.Required || locator.Required,
		},
	}
}

func (h *handler) describeXWorkmateSecrets(ctx context.Context, access *xworkmateAccessContext, user *store.User, profile *store.XWorkmateProfile) ([]gin.H, map[string]bool, error) {
	profileUserID := resolvedXWorkmateProfileUserID(access, user)
	secrets := make([]gin.H, 0, len(xworkmateManagedSecretTargets))
	statusByTarget := make(map[string]bool, len(xworkmateManagedSecretTargets))

	for _, managedTarget := range xworkmateManagedSecretTargets {
		locator, ok := findStoredXWorkmateSecretLocator(profile, managedTarget.Target)
		if !ok {
			var err error
			locator, err = buildManagedXWorkmateSecretLocator(access, profileUserID, managedTarget.Target)
			if err != nil {
				return nil, nil, err
			}
		}

		configured := false
		if h.xworkmateVaultService != nil {
			var err error
			configured, err = h.xworkmateVaultService.HasSecret(ctx, locator)
			if err != nil {
				return nil, nil, err
			}
		} else {
			configured = statusByTargetFromMetadata(profile, managedTarget.Target)
		}

		statusByTarget[managedTarget.Target] = configured
		secrets = append(secrets, buildXWorkmateSecretStatusPayload(locator, configured))
	}

	return secrets, statusByTarget, nil
}

func statusByTargetFromMetadata(profile *store.XWorkmateProfile, target string) bool {
	if profile == nil {
		return false
	}
	if target == store.XWorkmateSecretLocatorTargetOpenclawGatewayToken {
		return hasOpenclawXWorkmateSecretLocator(profile)
	}
	for _, locator := range profile.SecretLocators {
		if locator.Target == strings.ToLower(strings.TrimSpace(target)) &&
			strings.TrimSpace(locator.SecretPath) != "" &&
			strings.TrimSpace(locator.SecretKey) != "" {
			return true
		}
	}
	return false
}

func containsForbiddenXWorkmateTokenField(value any) bool {
	switch typed := value.(type) {
	case map[string]any:
		for key, nested := range typed {
			if _, ok := xworkmateForbiddenTokenFields[strings.ToLower(strings.TrimSpace(key))]; ok {
				return true
			}
			if containsForbiddenXWorkmateTokenField(nested) {
				return true
			}
		}
	case []any:
		for _, nested := range typed {
			if containsForbiddenXWorkmateTokenField(nested) {
				return true
			}
		}
	}
	return false
}

func (h *handler) getXWorkmateProfile(c *gin.Context) {
	user, ok := h.currentAuthenticatedUser(c)
	if !ok {
		return
	}

	access, err := h.resolveXWorkmateAccess(c.Request.Context(), h.resolveTenantHost(c), user)
	if err != nil {
		if errors.Is(err, store.ErrTenantMembershipNotFound) {
			respondError(c, http.StatusForbidden, "tenant_membership_required", "tenant membership is required")
			return
		}
		if errors.Is(err, store.ErrTenantNotFound) {
			respondError(c, http.StatusNotFound, "tenant_not_found", "tenant was not found")
			return
		}
		respondError(c, http.StatusInternalServerError, "xworkmate_context_failed", "failed to resolve xworkmate context")
		return
	}

	profile, err := h.loadXWorkmateProfile(c.Request.Context(), access, user)
	if err != nil {
		respondError(c, http.StatusInternalServerError, "xworkmate_profile_read_failed", "failed to load xworkmate profile")
		return
	}

	tokenConfigured := buildXWorkmateTokenConfigured(profile)
	if h.xworkmateVaultService != nil {
		_, statusByTarget, err := h.describeXWorkmateSecrets(c.Request.Context(), access, user, profile)
		if err == nil {
			tokenConfigured = buildXWorkmateTokenConfiguredWithVaultStatus(profile, statusByTarget)
		}
	}

	c.JSON(http.StatusOK, buildXWorkmateProfileResponse(access, profile, tokenConfigured))
}

func (h *handler) updateXWorkmateProfile(c *gin.Context) {
	user, ok := h.currentAuthenticatedUser(c)
	if !ok {
		return
	}

	access, err := h.resolveXWorkmateAccess(c.Request.Context(), h.resolveTenantHost(c), user)
	if err != nil {
		if errors.Is(err, store.ErrTenantMembershipNotFound) {
			respondError(c, http.StatusForbidden, "tenant_membership_required", "tenant membership is required")
			return
		}
		if errors.Is(err, store.ErrTenantNotFound) {
			respondError(c, http.StatusNotFound, "tenant_not_found", "tenant was not found")
			return
		}
		respondError(c, http.StatusInternalServerError, "xworkmate_context_failed", "failed to resolve xworkmate context")
		return
	}

	if !access.CanEditIntegrations {
		respondError(c, http.StatusForbidden, "xworkmate_profile_forbidden", "you are not allowed to update integrations for this tenant")
		return
	}
	if h.isReadOnlyAccount(user) {
		respondError(c, http.StatusForbidden, "read_only_account", "demo account is read-only")
		return
	}

	var raw map[string]any
	if err := c.ShouldBindJSON(&raw); err != nil {
		respondError(c, http.StatusBadRequest, "invalid_request", "invalid request payload")
		return
	}

	if containsForbiddenXWorkmateTokenField(raw) {
		respondError(c, http.StatusBadRequest, "token_persistence_forbidden", "raw token fields cannot be persisted")
		return
	}

	profileValue, ok := raw["profile"]
	if !ok {
		profileValue = raw
	}

	encodedProfile, err := json.Marshal(profileValue)
	if err != nil {
		respondError(c, http.StatusBadRequest, "invalid_request", "invalid profile payload")
		return
	}

	var payload xworkmateProfilePayload
	if err := json.Unmarshal(encodedProfile, &payload); err != nil {
		respondError(c, http.StatusBadRequest, "invalid_request", "invalid profile payload")
		return
	}

	profileUserID := resolvedXWorkmateProfileUserID(access, user)

	profile := &store.XWorkmateProfile{
		TenantID:        access.Tenant.ID,
		UserID:          profileUserID,
		Scope:           access.ProfileScope,
		OpenclawURL:     payload.OpenclawURL,
		OpenclawOrigin:  payload.OpenclawOrigin,
		VaultURL:        payload.VaultURL,
		VaultNamespace:  payload.VaultNamespace,
		VaultSecretPath: payload.VaultSecretPath,
		VaultSecretKey:  payload.VaultSecretKey,
		SecretLocators:  buildStoreXWorkmateSecretLocators(payload.SecretLocators),
		ApisixURL:       payload.ApisixURL,
	}
	if err := h.store.UpsertXWorkmateProfile(c.Request.Context(), profile); err != nil {
		respondError(c, http.StatusInternalServerError, "xworkmate_profile_write_failed", "failed to save xworkmate profile")
		return
	}

	tokenConfigured := buildXWorkmateTokenConfigured(profile)
	if h.xworkmateVaultService != nil {
		_, statusByTarget, err := h.describeXWorkmateSecrets(c.Request.Context(), access, user, profile)
		if err != nil {
			respondError(c, http.StatusInternalServerError, "xworkmate_secret_read_failed", "failed to load xworkmate secret status")
			return
		}
		tokenConfigured = buildXWorkmateTokenConfiguredWithVaultStatus(profile, statusByTarget)
	}

	c.JSON(http.StatusOK, buildXWorkmateProfileResponse(access, profile, tokenConfigured))
}

func (h *handler) getXWorkmateSecrets(c *gin.Context) {
	if !h.ensureXWorkmateVaultService(c) {
		return
	}

	user, ok := h.currentAuthenticatedUser(c)
	if !ok {
		return
	}

	access, err := h.resolveXWorkmateAccess(c.Request.Context(), h.resolveTenantHost(c), user)
	if err != nil {
		if errors.Is(err, store.ErrTenantMembershipNotFound) {
			respondError(c, http.StatusForbidden, "tenant_membership_required", "tenant membership is required")
			return
		}
		if errors.Is(err, store.ErrTenantNotFound) {
			respondError(c, http.StatusNotFound, "tenant_not_found", "tenant was not found")
			return
		}
		respondError(c, http.StatusInternalServerError, "xworkmate_context_failed", "failed to resolve xworkmate context")
		return
	}

	profile, err := h.loadXWorkmateProfile(c.Request.Context(), access, user)
	if err != nil {
		respondError(c, http.StatusInternalServerError, "xworkmate_profile_read_failed", "failed to load xworkmate profile")
		return
	}

	secrets, statusByTarget, err := h.describeXWorkmateSecrets(c.Request.Context(), access, user, profile)
	if err != nil {
		respondError(c, http.StatusInternalServerError, "xworkmate_secret_read_failed", "failed to load xworkmate secret status")
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"edition":             access.Tenant.Edition,
		"profileScope":        access.ProfileScope,
		"membershipRole":      access.MembershipRole,
		"canEditIntegrations": access.CanEditIntegrations,
		"canManageTenant":     access.CanManageTenant,
		"tenant":              gin.H{"id": access.Tenant.ID, "name": access.Tenant.Name, "domain": access.Domain},
		"secrets":             secrets,
		"tokenConfigured":     buildXWorkmateTokenConfiguredWithVaultStatus(profile, statusByTarget),
		"vaultBackendEnabled": true,
	})
}

func (h *handler) putXWorkmateSecret(c *gin.Context) {
	if !h.ensureXWorkmateVaultService(c) {
		return
	}

	user, ok := h.currentAuthenticatedUser(c)
	if !ok {
		return
	}

	access, err := h.resolveXWorkmateAccess(c.Request.Context(), h.resolveTenantHost(c), user)
	if err != nil {
		if errors.Is(err, store.ErrTenantMembershipNotFound) {
			respondError(c, http.StatusForbidden, "tenant_membership_required", "tenant membership is required")
			return
		}
		if errors.Is(err, store.ErrTenantNotFound) {
			respondError(c, http.StatusNotFound, "tenant_not_found", "tenant was not found")
			return
		}
		respondError(c, http.StatusInternalServerError, "xworkmate_context_failed", "failed to resolve xworkmate context")
		return
	}
	if !access.CanEditIntegrations {
		respondError(c, http.StatusForbidden, "xworkmate_secret_forbidden", "you are not allowed to update integrations for this tenant")
		return
	}
	if h.isReadOnlyAccount(user) {
		respondError(c, http.StatusForbidden, "read_only_account", "demo account is read-only")
		return
	}

	target := strings.ToLower(strings.TrimSpace(c.Param("target")))
	if _, ok := findXWorkmateManagedTarget(target); !ok {
		respondError(c, http.StatusBadRequest, "xworkmate_secret_unknown_target", "unknown xworkmate secret target")
		return
	}

	var payload struct {
		Value string `json:"value"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil {
		respondError(c, http.StatusBadRequest, "invalid_request", "invalid request payload")
		return
	}
	if strings.TrimSpace(payload.Value) == "" {
		respondError(c, http.StatusBadRequest, "xworkmate_secret_value_required", "secret value is required")
		return
	}

	profile, err := h.loadXWorkmateProfile(c.Request.Context(), access, user)
	if err != nil {
		respondError(c, http.StatusInternalServerError, "xworkmate_profile_read_failed", "failed to load xworkmate profile")
		return
	}
	if profile == nil {
		profile = &store.XWorkmateProfile{
			TenantID: access.Tenant.ID,
			UserID:   resolvedXWorkmateProfileUserID(access, user),
			Scope:    access.ProfileScope,
		}
	}

	locator, err := buildManagedXWorkmateSecretLocator(access, resolvedXWorkmateProfileUserID(access, user), target)
	if err != nil {
		respondError(c, http.StatusBadRequest, "xworkmate_secret_unknown_target", "unknown xworkmate secret target")
		return
	}
	if err := h.xworkmateVaultService.WriteSecret(c.Request.Context(), locator, payload.Value); err != nil {
		respondError(c, http.StatusInternalServerError, "xworkmate_secret_write_failed", "failed to persist xworkmate secret")
		return
	}

	upsertXWorkmateSecretLocator(profile, locator)
	if err := h.store.UpsertXWorkmateProfile(c.Request.Context(), profile); err != nil {
		respondError(c, http.StatusInternalServerError, "xworkmate_profile_write_failed", "failed to save xworkmate profile")
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"secret":          buildXWorkmateSecretStatusPayload(locator, true),
		"profileScope":    access.ProfileScope,
		"tokenConfigured": buildXWorkmateTokenConfiguredWithVaultStatus(profile, map[string]bool{target: true}),
	})
}

func (h *handler) deleteXWorkmateSecret(c *gin.Context) {
	if !h.ensureXWorkmateVaultService(c) {
		return
	}

	user, ok := h.currentAuthenticatedUser(c)
	if !ok {
		return
	}

	access, err := h.resolveXWorkmateAccess(c.Request.Context(), h.resolveTenantHost(c), user)
	if err != nil {
		if errors.Is(err, store.ErrTenantMembershipNotFound) {
			respondError(c, http.StatusForbidden, "tenant_membership_required", "tenant membership is required")
			return
		}
		if errors.Is(err, store.ErrTenantNotFound) {
			respondError(c, http.StatusNotFound, "tenant_not_found", "tenant was not found")
			return
		}
		respondError(c, http.StatusInternalServerError, "xworkmate_context_failed", "failed to resolve xworkmate context")
		return
	}
	if !access.CanEditIntegrations {
		respondError(c, http.StatusForbidden, "xworkmate_secret_forbidden", "you are not allowed to update integrations for this tenant")
		return
	}
	if h.isReadOnlyAccount(user) {
		respondError(c, http.StatusForbidden, "read_only_account", "demo account is read-only")
		return
	}

	target := strings.ToLower(strings.TrimSpace(c.Param("target")))
	if _, ok := findXWorkmateManagedTarget(target); !ok {
		respondError(c, http.StatusBadRequest, "xworkmate_secret_unknown_target", "unknown xworkmate secret target")
		return
	}

	profile, err := h.loadXWorkmateProfile(c.Request.Context(), access, user)
	if err != nil {
		respondError(c, http.StatusInternalServerError, "xworkmate_profile_read_failed", "failed to load xworkmate profile")
		return
	}

	locator, ok := findStoredXWorkmateSecretLocator(profile, target)
	if !ok {
		locator, err = buildManagedXWorkmateSecretLocator(access, resolvedXWorkmateProfileUserID(access, user), target)
		if err != nil {
			respondError(c, http.StatusBadRequest, "xworkmate_secret_unknown_target", "unknown xworkmate secret target")
			return
		}
	}

	if err := h.xworkmateVaultService.DeleteSecret(c.Request.Context(), locator); err != nil {
		respondError(c, http.StatusInternalServerError, "xworkmate_secret_delete_failed", "failed to delete xworkmate secret")
		return
	}

	tokenConfigured := buildXWorkmateTokenConfigured(profile)
	if h.xworkmateVaultService != nil {
		_, statusByTarget, err := h.describeXWorkmateSecrets(c.Request.Context(), access, user, profile)
		if err != nil {
			respondError(c, http.StatusInternalServerError, "xworkmate_secret_read_failed", "failed to load xworkmate secret status")
			return
		}
		tokenConfigured = buildXWorkmateTokenConfiguredWithVaultStatus(profile, statusByTarget)
	}

	c.JSON(http.StatusOK, gin.H{
		"secret":          buildXWorkmateSecretStatusPayload(locator, false),
		"profileScope":    access.ProfileScope,
		"tokenConfigured": tokenConfigured,
	})
}

func (h *handler) bootstrapTenant(c *gin.Context) {
	adminUser, ok := h.requireAdminPermission(c, permissionAdminSettingsWrite)
	if !ok {
		return
	}
	if !h.isRootAccount(adminUser) {
		respondError(c, http.StatusForbidden, "root_only", "root only")
		return
	}

	var payload struct {
		Name        string `json:"name"`
		AdminUserID string `json:"adminUserId"`
		AdminEmail  string `json:"adminEmail"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil {
		respondError(c, http.StatusBadRequest, "invalid_request", "invalid request payload")
		return
	}

	var member *store.User
	var err error
	switch {
	case strings.TrimSpace(payload.AdminUserID) != "":
		member, err = h.store.GetUserByID(c.Request.Context(), strings.TrimSpace(payload.AdminUserID))
	case strings.TrimSpace(payload.AdminEmail) != "":
		member, err = h.store.GetUserByEmail(c.Request.Context(), strings.TrimSpace(payload.AdminEmail))
	default:
		respondError(c, http.StatusBadRequest, "admin_user_required", "adminUserId or adminEmail is required")
		return
	}
	if err != nil {
		respondError(c, http.StatusNotFound, "admin_user_not_found", "admin user not found")
		return
	}

	domain, err := store.GenerateRandomTenantDomain()
	if err != nil {
		respondError(c, http.StatusInternalServerError, "tenant_domain_generation_failed", "failed to generate tenant domain")
		return
	}

	tenant := &store.Tenant{
		Name:    strings.TrimSpace(payload.Name),
		Edition: store.TenantPrivateEdition,
	}
	if tenant.Name == "" {
		tenant.Name = member.Name
		if tenant.Name == "" {
			tenant.Name = member.Email
		}
	}
	if err := h.store.EnsureTenant(c.Request.Context(), tenant); err != nil {
		respondError(c, http.StatusInternalServerError, "tenant_create_failed", "failed to create tenant")
		return
	}
	if err := h.store.EnsureTenantDomain(c.Request.Context(), &store.TenantDomain{
		TenantID:  tenant.ID,
		Domain:    domain,
		Kind:      store.TenantDomainKindGenerated,
		IsPrimary: true,
		Status:    store.TenantDomainStatusVerified,
	}); err != nil {
		respondError(c, http.StatusInternalServerError, "tenant_domain_create_failed", "failed to create tenant domain")
		return
	}
	if err := h.store.UpsertTenantMembership(c.Request.Context(), &store.TenantMembership{
		TenantID: tenant.ID,
		UserID:   member.ID,
		Role:     store.TenantMembershipRoleAdmin,
	}); err != nil {
		respondError(c, http.StatusInternalServerError, "tenant_membership_create_failed", "failed to create tenant membership")
		return
	}

	c.JSON(http.StatusCreated, gin.H{
		"tenant": gin.H{
			"id":      tenant.ID,
			"name":    tenant.Name,
			"edition": tenant.Edition,
			"domain":  domain,
		},
		"member": gin.H{
			"id":    member.ID,
			"email": member.Email,
			"role":  store.TenantMembershipRoleAdmin,
		},
	})
}
