package organization

import (
	"context"
	"fmt"
	"slices"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/core/identifiers"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
)

func (txs TxStore) GetOrganizationHostedModelSettingsForUser(ctx context.Context, userID string, organizationID string) (OrganizationHostedModelSettingsView, error) {
	if _, err := txs.requireActiveOrganizationAdmin(ctx, txs.db, userID, organizationID); err != nil {
		return OrganizationHostedModelSettingsView{}, err
	}
	return txs.loadOrganizationHostedModelSettings(ctx, txs.db, organizationID)
}

func (txs TxStore) PatchOrganizationHostedModelSettingsForUser(ctx context.Context, userID string, organizationID string, params UpdateOrganizationHostedModelSettingsParams) (OrganizationHostedModelSettingsView, error) {
	if _, err := txs.requireActiveOrganizationAdmin(ctx, txs.db, userID, organizationID); err != nil {
		return OrganizationHostedModelSettingsView{}, err
	}

	return withTxValue(ctx, txs.db, func(tx coredb.Tx) (OrganizationHostedModelSettingsView, error) {
		current, err := txs.loadOrganizationHostedModelSettings(ctx, tx, organizationID)
		if err != nil {
			return OrganizationHostedModelSettingsView{}, err
		}
		next, changed, err := resolveOrganizationHostedModelSettingsUpdate(current, params)
		if err != nil {
			return OrganizationHostedModelSettingsView{}, err
		}
		if !changed {
			return current, nil
		}

		modelRefsJSON, err := encodeJSONStringList(next.AutoAuthorizationModelRefs)
		if err != nil {
			return OrganizationHostedModelSettingsView{}, fmt.Errorf("encode hosted auto authorization model refs: %w", err)
		}
		now := time.Now().UTC()
		if _, err := tx.Exec(ctx, `
UPDATE organization_settings
SET hosted_model_picker_visibility_policy = $2,
    hosted_model_auto_authorization_enabled = $3,
    hosted_model_auto_authorization_mode = $4,
    hosted_model_auto_authorization_model_refs_json = CAST(NULLIF($5, '') AS JSON),
    updated_at = $6
WHERE organization_id = $1
  AND status = 'active'
`, organizationID, next.PickerVisibilityPolicy, next.AutoAuthorizationEnabled, next.AutoAuthorizationMode, modelRefsJSON, now); err != nil {
			return OrganizationHostedModelSettingsView{}, fmt.Errorf("update organization hosted model settings: %w", err)
		}
		return txs.loadOrganizationHostedModelSettings(ctx, tx, organizationID)
	})
}

func (txs TxStore) GetOrganizationWorkspaceHostedModelAuthorizationForUser(ctx context.Context, userID string, organizationID string, workspaceID string) (OrganizationWorkspaceHostedModelAuthorizationView, error) {
	if _, err := txs.requireActiveOrganizationAdmin(ctx, txs.db, userID, organizationID); err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, err
	}
	if err := txs.requireOrganizationWorkspaceBinding(ctx, txs.db, organizationID, workspaceID); err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, err
	}

	settings, err := txs.loadOrganizationHostedModelSettings(ctx, txs.db, organizationID)
	if err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, err
	}
	view, _, err := txs.loadEffectiveOrganizationWorkspaceHostedModelAuthorization(ctx, txs.db, organizationID, workspaceID, settings)
	return view, err
}

func (txs TxStore) GetHostedModelSelectionForWorkspace(ctx context.Context, organizationID string, workspaceID string) (OrganizationHostedModelSelectionView, error) {
	trimmedOrganizationID := strings.TrimSpace(organizationID)
	trimmedWorkspaceID := strings.TrimSpace(workspaceID)
	if trimmedOrganizationID == "" {
		return OrganizationHostedModelSelectionView{}, badRequest("organizationId is required")
	}
	if trimmedWorkspaceID == "" {
		return OrganizationHostedModelSelectionView{}, badRequest("workspaceId is required")
	}
	if err := txs.requireOrganizationWorkspaceBinding(ctx, txs.db, trimmedOrganizationID, trimmedWorkspaceID); err != nil {
		return OrganizationHostedModelSelectionView{}, err
	}

	settings, err := txs.loadOrganizationHostedModelSettings(ctx, txs.db, trimmedOrganizationID)
	if err != nil {
		return OrganizationHostedModelSelectionView{}, err
	}
	authorization, _, err := txs.loadEffectiveOrganizationWorkspaceHostedModelAuthorization(ctx, txs.db, trimmedOrganizationID, trimmedWorkspaceID, settings)
	if err != nil {
		return OrganizationHostedModelSelectionView{}, err
	}
	return OrganizationHostedModelSelectionView{
		OrganizationID:          trimmedOrganizationID,
		WorkspaceID:             trimmedWorkspaceID,
		PickerVisibilityPolicy:  settings.PickerVisibilityPolicy,
		AuthorizationMode:       authorization.AuthorizationMode,
		SelectedModelRefs:       append([]string(nil), authorization.SelectedModelRefs...),
		AuthorizationStatus:     authorization.Status,
		AuthorizationConfigured: authorization.Configured,
		SettingsUpdatedAt:       settings.UpdatedAt,
		AuthorizationUpdatedAt:  authorization.UpdatedAt,
		AuthorizationCreatedAt:  authorization.CreatedAt,
		AuthorizedByUserID:      authorization.AuthorizedByUserID,
	}, nil
}

func (txs TxStore) UpsertOrganizationWorkspaceHostedModelAuthorizationForUser(ctx context.Context, userID string, organizationID string, workspaceID string, params UpsertOrganizationWorkspaceHostedModelAuthorizationParams) (OrganizationWorkspaceHostedModelAuthorizationView, error) {
	if _, err := txs.requireActiveOrganizationAdmin(ctx, txs.db, userID, organizationID); err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, err
	}

	return withTxValue(ctx, txs.db, func(tx coredb.Tx) (OrganizationWorkspaceHostedModelAuthorizationView, error) {
		return txs.upsertOrganizationWorkspaceHostedModelAuthorizationInTx(ctx, tx, organizationID, workspaceID, userID, params)
	})
}

func (txs TxStore) PreviewOrganizationWorkspaceHostedModelAuthorizationForUser(ctx context.Context, userID string, organizationID string, workspaceID string, params UpsertOrganizationWorkspaceHostedModelAuthorizationParams) (OrganizationWorkspaceHostedModelAuthorizationView, error) {
	if _, err := txs.requireActiveOrganizationAdmin(ctx, txs.db, userID, organizationID); err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, err
	}
	if err := txs.requireOrganizationWorkspaceBinding(ctx, txs.db, organizationID, workspaceID); err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, err
	}

	nextMode, nextStatus, nextModelRefs, err := resolveWorkspaceHostedAuthorization(params)
	if err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, err
	}
	settings, err := txs.loadOrganizationHostedModelSettings(ctx, txs.db, organizationID)
	if err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, err
	}
	current, _, err := txs.loadEffectiveOrganizationWorkspaceHostedModelAuthorization(ctx, txs.db, organizationID, workspaceID, settings)
	if err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, err
	}
	affectedCount, err := txs.countAffectedHostedAgentsForAuthorizationChange(ctx, txs.db, workspaceID, current.AuthorizationMode, current.SelectedModelRefs, nextMode, nextModelRefs)
	if err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, err
	}

	current.AuthorizationMode = nextMode
	current.SelectedModelRefs = nextModelRefs
	current.Status = nextStatus
	current.Configured = true
	current.AuthorizedByUserID = strings.TrimSpace(userID)
	current.AffectedHostedAgentCount = affectedCount
	return current, nil
}

func (txs TxStore) applyHostedAutoAuthorizationToWorkspaceIfEnabled(ctx context.Context, tx coredb.Tx, organizationID string, workspaceID string, userID string) error {
	settings, err := txs.loadOrganizationHostedModelSettings(ctx, tx, organizationID)
	if err != nil {
		return err
	}
	if !settings.AutoAuthorizationEnabled {
		return nil
	}
	_, err = txs.upsertOrganizationWorkspaceHostedModelAuthorizationInTx(ctx, tx, organizationID, workspaceID, userID, UpsertOrganizationWorkspaceHostedModelAuthorizationParams{
		AuthorizationMode: settings.AutoAuthorizationMode,
		SelectedModelRefs: settings.AutoAuthorizationModelRefs,
	})
	return err
}

func (txs TxStore) bootstrapInitialDefaultWorkspaceHostedAuthorizationInTx(ctx context.Context, tx coredb.Tx, organizationID string, workspaceID string, userID string) error {
	_, err := txs.upsertOrganizationWorkspaceHostedModelAuthorizationInTx(ctx, tx, organizationID, workspaceID, userID, UpsertOrganizationWorkspaceHostedModelAuthorizationParams{
		AuthorizationMode: organizationHostedAuthorizationModeAllActive,
	})
	if err != nil {
		return fmt.Errorf("bootstrap default workspace hosted authorization: %w", err)
	}
	return nil
}

func (txs TxStore) loadOrganizationHostedModelSettings(ctx context.Context, queryer queryRower, organizationID string) (OrganizationHostedModelSettingsView, error) {
	row := queryer.QueryRow(ctx, `
SELECT organization_id,
       hosted_model_picker_visibility_policy,
       hosted_model_auto_authorization_enabled,
       hosted_model_auto_authorization_mode,
       COALESCE(hosted_model_auto_authorization_model_refs_json, CAST('[]' AS JSON)),
       updated_at
FROM organization_settings
WHERE organization_id = $1
  AND status = 'active'
`, organizationID)

	var view OrganizationHostedModelSettingsView
	var autoModelRefsRaw []byte
	if err := row.Scan(
		&view.OrganizationID,
		&view.PickerVisibilityPolicy,
		&view.AutoAuthorizationEnabled,
		&view.AutoAuthorizationMode,
		&autoModelRefsRaw,
		&view.UpdatedAt,
	); err != nil {
		if coredb.IsNotFound(err) {
			return OrganizationHostedModelSettingsView{}, ErrForbidden
		}
		return OrganizationHostedModelSettingsView{}, fmt.Errorf("load organization hosted model settings: %w", err)
	}

	autoModelRefs, err := decodeJSONStringList(autoModelRefsRaw)
	if err != nil {
		return OrganizationHostedModelSettingsView{}, fmt.Errorf("decode hosted auto authorization model refs: %w", err)
	}
	view.PickerVisibilityPolicy = hostedPickerVisibilityOrDefault(view.PickerVisibilityPolicy)
	view.AutoAuthorizationMode = hostedAuthorizationModeOrDefault(view.AutoAuthorizationMode)
	view.AutoAuthorizationModelRefs = autoModelRefs
	if view.AutoAuthorizationMode == organizationHostedAuthorizationModeAllActive {
		view.AutoAuthorizationModelRefs = []string{}
	}
	return view, nil
}

func resolveOrganizationHostedModelSettingsUpdate(current OrganizationHostedModelSettingsView, params UpdateOrganizationHostedModelSettingsParams) (OrganizationHostedModelSettingsView, bool, error) {
	next := current
	changed := false

	if params.PickerVisibilityPolicy != nil {
		resolved, err := resolveHostedPickerVisibility(*params.PickerVisibilityPolicy)
		if err != nil {
			return OrganizationHostedModelSettingsView{}, false, err
		}
		if next.PickerVisibilityPolicy != resolved {
			changed = true
		}
		next.PickerVisibilityPolicy = resolved
	}
	if params.AutoAuthorizationEnabled != nil {
		if next.AutoAuthorizationEnabled != *params.AutoAuthorizationEnabled {
			changed = true
		}
		next.AutoAuthorizationEnabled = *params.AutoAuthorizationEnabled
	}
	if params.AutoAuthorizationMode != nil {
		resolved, err := resolveHostedAuthorizationMode(*params.AutoAuthorizationMode, false)
		if err != nil {
			return OrganizationHostedModelSettingsView{}, false, err
		}
		if next.AutoAuthorizationMode != resolved {
			changed = true
		}
		next.AutoAuthorizationMode = resolved
	}
	if params.AutoAuthorizationModelRefs != nil {
		normalized := normalizeStringList(*params.AutoAuthorizationModelRefs)
		if !slices.Equal(next.AutoAuthorizationModelRefs, normalized) {
			changed = true
		}
		next.AutoAuthorizationModelRefs = normalized
	}
	if next.AutoAuthorizationMode == organizationHostedAuthorizationModeAllActive {
		if len(next.AutoAuthorizationModelRefs) > 0 {
			changed = true
		}
		next.AutoAuthorizationModelRefs = []string{}
	}
	if next.AutoAuthorizationEnabled && next.AutoAuthorizationMode == organizationHostedAuthorizationModeSelected && len(next.AutoAuthorizationModelRefs) == 0 {
		return OrganizationHostedModelSettingsView{}, false, badRequest("selected hosted model auto-authorization requires model references")
	}
	return next, changed, nil
}

func (txs TxStore) upsertOrganizationWorkspaceHostedModelAuthorizationInTx(ctx context.Context, tx coredb.Tx, organizationID string, workspaceID string, userID string, params UpsertOrganizationWorkspaceHostedModelAuthorizationParams) (OrganizationWorkspaceHostedModelAuthorizationView, error) {
	if err := txs.requireOrganizationWorkspaceBinding(ctx, tx, organizationID, workspaceID); err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, err
	}

	nextMode, nextStatus, nextModelRefs, err := resolveWorkspaceHostedAuthorization(params)
	if err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, err
	}
	settings, err := txs.loadOrganizationHostedModelSettings(ctx, tx, organizationID)
	if err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, err
	}
	current, _, err := txs.loadEffectiveOrganizationWorkspaceHostedModelAuthorization(ctx, tx, organizationID, workspaceID, settings)
	if err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, err
	}
	affectedCount, err := txs.countAffectedHostedAgentsForAuthorizationChange(ctx, tx, workspaceID, current.AuthorizationMode, current.SelectedModelRefs, nextMode, nextModelRefs)
	if err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, err
	}

	modelRefsJSON, err := encodeJSONStringList(nextModelRefs)
	if err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, fmt.Errorf("encode hosted workspace authorization model refs: %w", err)
	}
	authorizationID, err := identifiers.NewEntityID("owha")
	if err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, fmt.Errorf("generate hosted workspace authorization id: %w", err)
	}
	now := time.Now().UTC()
	if err := execPortableUpdateThenInsert(
		ctx,
		tx,
		`
SELECT 1
FROM organization_workspace_hosted_model_authorizations
WHERE workspace_id = $1
`,
		[]any{workspaceID},
		`
UPDATE organization_workspace_hosted_model_authorizations
SET authorization_mode = $3,
    selected_model_refs_json = CAST(NULLIF($4, '') AS JSON),
    status = $5,
    authorized_by_user_id = $6,
    updated_at = $7
WHERE organization_id = $1
  AND workspace_id = $2
`,
		[]any{organizationID, workspaceID, nextMode, modelRefsJSON, nextStatus, userID, now},
		`
INSERT INTO organization_workspace_hosted_model_authorizations (
    id,
    organization_id,
    workspace_id,
    authorization_mode,
    selected_model_refs_json,
    status,
    authorized_by_user_id,
    created_at,
    updated_at
) VALUES ($1, $2, $3, $4, CAST(NULLIF($5, '') AS JSON), $6, $7, $8, $8)
`,
		[]any{authorizationID, organizationID, workspaceID, nextMode, modelRefsJSON, nextStatus, userID, now},
	); err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, fmt.Errorf("upsert hosted workspace authorization: %w", err)
	}

	view, _, err := txs.loadOrganizationWorkspaceHostedModelAuthorization(ctx, tx, organizationID, workspaceID)
	if err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, err
	}
	view.AffectedHostedAgentCount = affectedCount
	return view, nil
}

func (txs TxStore) loadOrganizationWorkspaceHostedModelAuthorization(ctx context.Context, queryer queryRower, organizationID string, workspaceID string) (OrganizationWorkspaceHostedModelAuthorizationView, bool, error) {
	row := queryer.QueryRow(ctx, `
SELECT organization_id,
       workspace_id,
       authorization_mode,
       COALESCE(selected_model_refs_json, CAST('[]' AS JSON)),
       status,
       authorized_by_user_id,
       created_at,
       updated_at
FROM organization_workspace_hosted_model_authorizations
WHERE organization_id = $1
  AND workspace_id = $2
`, organizationID, workspaceID)

	var view OrganizationWorkspaceHostedModelAuthorizationView
	var selectedModelRefsRaw []byte
	if err := row.Scan(
		&view.OrganizationID,
		&view.WorkspaceID,
		&view.AuthorizationMode,
		&selectedModelRefsRaw,
		&view.Status,
		&view.AuthorizedByUserID,
		&view.CreatedAt,
		&view.UpdatedAt,
	); err != nil {
		if coredb.IsNotFound(err) {
			return defaultWorkspaceHostedAuthorizationView(organizationID, workspaceID), false, nil
		}
		return OrganizationWorkspaceHostedModelAuthorizationView{}, false, fmt.Errorf("load hosted workspace authorization: %w", err)
	}

	selectedModelRefs, err := decodeJSONStringList(selectedModelRefsRaw)
	if err != nil {
		return OrganizationWorkspaceHostedModelAuthorizationView{}, false, fmt.Errorf("decode hosted workspace authorization model refs: %w", err)
	}
	view.Configured = true
	view.Status = hostedAuthorizationStatusOrDefault(view.Status)
	if view.Status != organizationHostedAuthorizationStatusActive {
		view.AuthorizationMode = organizationHostedAuthorizationModeUnauthorized
		view.SelectedModelRefs = []string{}
		return view, true, nil
	}
	view.AuthorizationMode = hostedAuthorizationModeOrDefault(view.AuthorizationMode)
	if view.AuthorizationMode == organizationHostedAuthorizationModeAllActive {
		view.SelectedModelRefs = []string{}
	} else {
		view.SelectedModelRefs = selectedModelRefs
	}
	return view, true, nil
}

func (txs TxStore) loadEffectiveOrganizationWorkspaceHostedModelAuthorization(ctx context.Context, queryer queryRower, organizationID string, workspaceID string, settings OrganizationHostedModelSettingsView) (OrganizationWorkspaceHostedModelAuthorizationView, bool, error) {
	authorization, configured, err := txs.loadOrganizationWorkspaceHostedModelAuthorization(ctx, queryer, organizationID, workspaceID)
	if err != nil || configured {
		return authorization, configured, err
	}
	return defaultWorkspaceHostedAuthorizationViewFromSettings(organizationID, workspaceID, settings), false, nil
}

func defaultWorkspaceHostedAuthorizationView(organizationID string, workspaceID string) OrganizationWorkspaceHostedModelAuthorizationView {
	return OrganizationWorkspaceHostedModelAuthorizationView{
		OrganizationID:    strings.TrimSpace(organizationID),
		WorkspaceID:       strings.TrimSpace(workspaceID),
		AuthorizationMode: organizationHostedAuthorizationModeAllActive,
		SelectedModelRefs: []string{},
		Status:            organizationHostedAuthorizationStatusActive,
		Configured:        false,
	}
}

func defaultWorkspaceHostedAuthorizationViewFromSettings(organizationID string, workspaceID string, settings OrganizationHostedModelSettingsView) OrganizationWorkspaceHostedModelAuthorizationView {
	view := defaultWorkspaceHostedAuthorizationView(organizationID, workspaceID)
	if !settings.AutoAuthorizationEnabled {
		view.AuthorizationMode = organizationHostedAuthorizationModeUnauthorized
		view.Status = organizationHostedAuthorizationStatusInactive
		return view
	}

	view.AuthorizationMode = hostedAuthorizationModeOrDefault(settings.AutoAuthorizationMode)
	switch view.AuthorizationMode {
	case organizationHostedAuthorizationModeAllActive:
		view.SelectedModelRefs = []string{}
	case organizationHostedAuthorizationModeSelected:
		view.SelectedModelRefs = append([]string(nil), settings.AutoAuthorizationModelRefs...)
		if len(view.SelectedModelRefs) == 0 {
			view.AuthorizationMode = organizationHostedAuthorizationModeUnauthorized
			view.Status = organizationHostedAuthorizationStatusInactive
		}
	default:
		view.AuthorizationMode = organizationHostedAuthorizationModeUnauthorized
		view.SelectedModelRefs = []string{}
		view.Status = organizationHostedAuthorizationStatusInactive
	}
	return view
}

func resolveWorkspaceHostedAuthorization(params UpsertOrganizationWorkspaceHostedModelAuthorizationParams) (string, string, []string, error) {
	mode, err := resolveHostedAuthorizationMode(params.AuthorizationMode, true)
	if err != nil {
		return "", "", nil, err
	}
	switch mode {
	case organizationHostedAuthorizationModeUnauthorized:
		return mode, organizationHostedAuthorizationStatusInactive, []string{}, nil
	case organizationHostedAuthorizationModeAllActive:
		return mode, organizationHostedAuthorizationStatusActive, []string{}, nil
	case organizationHostedAuthorizationModeSelected:
		modelRefs := normalizeStringList(params.SelectedModelRefs)
		if len(modelRefs) == 0 {
			return "", "", nil, badRequest("selected hosted model authorization requires model references")
		}
		return mode, organizationHostedAuthorizationStatusActive, modelRefs, nil
	default:
		return "", "", nil, badRequest("invalid hosted workspace authorization mode")
	}
}

func (txs TxStore) countAffectedHostedAgentsForAuthorizationChange(ctx context.Context, db queryer, workspaceID string, currentMode string, currentModelRefs []string, nextMode string, nextModelRefs []string) (int, error) {
	if currentMode == nextMode && slices.Equal(currentModelRefs, nextModelRefs) {
		return 0, nil
	}

	rows, err := db.Query(ctx, `
SELECT a.model
FROM agents a
WHERE a.workspace_id = $1
  AND a.status <> 'archived'
  AND a.llm_mode = $2
`, workspaceID, organizationHostedAgentLLMMode)
	if err != nil {
		return 0, fmt.Errorf("list hosted agents affected by authorization change: %w", err)
	}
	defer rows.Close()

	affected := 0
	for rows.Next() {
		var modelRef string
		if err := rows.Scan(&modelRef); err != nil {
			return 0, fmt.Errorf("scan hosted agent model reference: %w", err)
		}
		modelRef = strings.TrimSpace(modelRef)
		if modelRef == "" {
			continue
		}
		if hostedAuthorizationCoversModel(currentMode, currentModelRefs, modelRef) && !hostedAuthorizationCoversModel(nextMode, nextModelRefs, modelRef) {
			affected++
		}
	}
	if err := rows.Err(); err != nil {
		return 0, fmt.Errorf("iterate hosted agent model references: %w", err)
	}
	return affected, nil
}

func hostedAuthorizationCoversModel(mode string, modelRefs []string, modelRef string) bool {
	switch hostedAuthorizationModeOrDefault(mode) {
	case organizationHostedAuthorizationModeAllActive:
		return true
	case organizationHostedAuthorizationModeSelected:
		return slices.Contains(normalizeStringList(modelRefs), strings.TrimSpace(modelRef))
	default:
		return false
	}
}

func hostedPickerVisibilityOrDefault(policy string) string {
	switch strings.TrimSpace(policy) {
	case organizationHostedPickerVisibilityHideDisabled:
		return organizationHostedPickerVisibilityHideDisabled
	default:
		return organizationHostedPickerVisibilityShowDisabled
	}
}

func resolveHostedPickerVisibility(policy string) (string, error) {
	switch strings.TrimSpace(policy) {
	case organizationHostedPickerVisibilityShowDisabled:
		return organizationHostedPickerVisibilityShowDisabled, nil
	case organizationHostedPickerVisibilityHideDisabled:
		return organizationHostedPickerVisibilityHideDisabled, nil
	default:
		return "", badRequest("invalid hosted model picker visibility policy")
	}
}

func hostedAuthorizationModeOrDefault(mode string) string {
	switch strings.TrimSpace(mode) {
	case "":
		return organizationHostedAuthorizationModeAllActive
	case organizationHostedAuthorizationModeAllActive:
		return organizationHostedAuthorizationModeAllActive
	case organizationHostedAuthorizationModeSelected:
		return organizationHostedAuthorizationModeSelected
	case organizationHostedAuthorizationModeUnauthorized:
		return organizationHostedAuthorizationModeUnauthorized
	default:
		return organizationHostedAuthorizationModeUnauthorized
	}
}

func resolveHostedAuthorizationMode(mode string, allowUnauthorized bool) (string, error) {
	switch strings.TrimSpace(mode) {
	case organizationHostedAuthorizationModeAllActive:
		return organizationHostedAuthorizationModeAllActive, nil
	case organizationHostedAuthorizationModeSelected:
		return organizationHostedAuthorizationModeSelected, nil
	case organizationHostedAuthorizationModeUnauthorized:
		if allowUnauthorized {
			return organizationHostedAuthorizationModeUnauthorized, nil
		}
		return "", badRequest("hosted auto-authorization cannot use workspace_not_authorized")
	default:
		return "", badRequest("invalid hosted model authorization mode")
	}
}

func hostedAuthorizationStatusOrDefault(status string) string {
	switch strings.TrimSpace(status) {
	case organizationHostedAuthorizationStatusInactive:
		return organizationHostedAuthorizationStatusInactive
	default:
		return organizationHostedAuthorizationStatusActive
	}
}

func normalizeStringList(items []string) []string {
	normalized := make([]string, 0, len(items))
	seen := make(map[string]struct{}, len(items))
	for _, item := range items {
		trimmed := strings.TrimSpace(item)
		if trimmed == "" {
			continue
		}
		if _, ok := seen[trimmed]; ok {
			continue
		}
		seen[trimmed] = struct{}{}
		normalized = append(normalized, trimmed)
	}
	return normalized
}
