package workspace

import (
	"context"
	"log/slog"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/api/dto"
)

const (
	selectableModelsObject = "selectable_model.list"

	selectableSourceBYOK   = "agentteam_proxy"
	selectableSourceHosted = "agentteam_hosted"

	hostedAvailabilityStatusAvailable   = "available"
	hostedAvailabilityStatusUnavailable = "unavailable"

	hostedPickerVisibilityHideDisabled = "hide_disabled_when_unavailable"
	hostedAuthorizationAllActive       = "all_active_hosted_inventory"
	hostedAuthorizationSelected        = "selected_hosted_models"

	hostedReasonWorkspaceNotAuthorized = "workspace_not_authorized"
	hostedReasonFeatureUnsupported     = "feature_unsupported"
	hostedReasonTemporarilyUnavailable = "temporarily_unavailable"
	hostedReasonAdminActionRequired    = "admin_action_required"

	selectableActionSelect                       = "select"
	selectableActionContactAdmin                 = "contact_admin"
	selectableActionManageHostedModels           = "manage_hosted_models"
	selectableActionManageWorkspaceAuthorization = "manage_workspace_authorization"
	selectableActionRechargeCredits              = "recharge_credits"

	hostedMachineCapabilityMaaSDirect = "agent:maas-direct"
)

// SelectableModels handles GET /api/v1/workspaces/:workspaceId/selectable-models.
// It combines workspace-visible BYOK provider models with AgentTeam-hosted models
// that have been resolved through MaaS availability and AgentTeam workspace access.
func (handler *WorkspaceHandler) SelectableModels(r api.WorkspaceScopedRequest) (api.Response, error) {
	response := dto.SelectableModelsResponse{
		Object:      selectableModelsObject,
		WorkspaceID: r.WorkspaceID(),
		Models:      []dto.SelectableModelOption{},
		HostedAvailability: dto.SelectableHostedAvailabilitySummary{
			Status: hostedAvailabilityStatusUnavailable,
		},
	}

	orgs, ok := handler.organizationProvider()
	if !ok {
		response.HostedAvailability.Reasons = []string{hostedReasonTemporarilyUnavailable}
		return api.OK(response), nil
	}

	providers, err := orgs.ListVisibleOrganizationProvidersForWorkspace(r.Context(), r.WorkspaceID())
	if err != nil {
		return api.Response{}, err
	}
	response.Models = append(response.Models, selectableBYOKModelOptions(providers)...)

	workspaceOrganization, err := orgs.GetWorkspaceOrganizationForUser(r.Context(), r.UserID(), r.WorkspaceID())
	if err != nil {
		return api.Response{}, err
	}
	selection, err := orgs.GetHostedModelSelectionForWorkspace(r.Context(), workspaceOrganization.ID, r.WorkspaceID())
	if err != nil {
		return api.Response{}, err
	}
	if handler.hostedModels == nil {
		response.HostedAvailability.Reasons = []string{hostedReasonTemporarilyUnavailable}
		return api.OK(response), nil
	}

	availability, err := handler.hostedModels.LoadHostedAvailability(r.Context(), workspaceOrganization.ID)
	if err != nil {
		logHostedAvailabilityFailure(r.Context(), handler.logger, workspaceOrganization.ID, r.WorkspaceID(), err)
		response.HostedAvailability.Reasons = []string{hostedReasonTemporarilyUnavailable}
		return api.OK(response), nil
	}

	pricing := HostedPricing{}
	if handler.hostedPricing != nil {
		loadedPricing, err := handler.hostedPricing.LoadHostedPricing(r.Context(), workspaceOrganization.ID)
		if err != nil {
			logHostedPricingFailure(r.Context(), handler.logger, workspaceOrganization.ID, r.WorkspaceID(), err)
		} else {
			pricing = loadedPricing
		}
	}

	response.HostedAvailability = dto.SelectableHostedAvailabilitySummary{
		Status:               hostedAvailabilityStatusAvailable,
		CreditAtomsPerCredit: pricing.CreditAtomsPerCredit,
	}
	response.Models = append(response.Models, selectableHostedModelOptions(availability, selection, selectableModelQuery{
		Features: parseSelectableFeatures(r.Gin.Query("features")),
		IsAdmin:  isOrganizationAdminRole(workspaceOrganization.Role),
	}, pricing)...)
	return api.OK(response), nil
}

type selectableModelQuery struct {
	Features []string
	IsAdmin  bool
}

func parseSelectableFeatures(raw string) []string {
	if strings.TrimSpace(raw) == "" {
		return nil
	}
	return normalizeSelectableReasons(strings.Split(raw, ","))
}

func isOrganizationAdminRole(role string) bool {
	switch strings.TrimSpace(role) {
	case "org_owner", "org_admin":
		return true
	default:
		return false
	}
}

func logHostedAvailabilityFailure(ctx context.Context, logger *slog.Logger, organizationID string, workspaceID string, err error) {
	if logger == nil {
		return
	}
	logger.WarnContext(
		ctx,
		"load hosted model availability",
		slog.String("organization_id", strings.TrimSpace(organizationID)),
		slog.String("workspace_id", strings.TrimSpace(workspaceID)),
		slog.String("error_class", "hosted_availability_unavailable"),
	)
}

func logHostedPricingFailure(ctx context.Context, logger *slog.Logger, organizationID string, workspaceID string, err error) {
	if logger == nil {
		return
	}
	logger.WarnContext(
		ctx,
		"load hosted model pricing",
		slog.String("organization_id", strings.TrimSpace(organizationID)),
		slog.String("workspace_id", strings.TrimSpace(workspaceID)),
		slog.String("error_class", "hosted_pricing_unavailable"),
	)
}
