package workspace

import (
	"context"
	"errors"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/api/dto"
	"github.com/tidbcloud/AgentTeam/internal/runtimecontrol"
	workspaceservice "github.com/tidbcloud/AgentTeam/internal/service/workspace"
)

// AccountBehaviorSettings handles GET /api/v1/me/behavior-settings.
// It keeps the legacy response shape for clients while reply enhancement is
// enabled by default.
func (handler *WorkspaceHandler) AccountBehaviorSettings(r api.AuthenticatedRequest) (api.Response, error) {
	settings, err := handler.workspaceService.AccountBehaviorSettingsForUser(r.Context(), r.UserID())
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(accountBehaviorSettingsResponse(settings)), nil
}

func syncMachineCatalogIgnoringOffline(ctx context.Context, runtime MachineRuntime, machineID string) error {
	if runtime == nil {
		return nil
	}
	trimmedMachineID := strings.TrimSpace(machineID)
	if trimmedMachineID == "" {
		return nil
	}
	err := runtime.SyncMachineCatalog(ctx, trimmedMachineID)
	if errors.Is(err, runtimecontrol.ErrMachineOffline) {
		return nil
	}
	return err
}

func managedWorkspaceIDs(ctx context.Context, service WorkspaceService, userID string) ([]string, error) {
	if service == nil {
		return nil, nil
	}
	workspaces, err := service.ListUserWorkspaces(ctx, userID)
	if err != nil {
		return nil, err
	}
	seen := make(map[string]struct{}, len(workspaces))
	ids := make([]string, 0, len(workspaces))
	for _, workspace := range workspaces {
		id := strings.TrimSpace(workspace.WorkspaceID)
		if id == "" {
			continue
		}
		if _, ok := seen[id]; ok {
			continue
		}
		seen[id] = struct{}{}
		ids = append(ids, id)
	}
	return ids, nil
}

// UpdateAccountBehaviorSettings handles PATCH /api/v1/me/behavior-settings.
// Reply enhancement is always-on: any requested VisibleReplyContractEnabled is
// accepted but ignored (a visible-only patch is a successful no-op). Only
// RuntimeDebugEnabled is still persisted.
func (handler *WorkspaceHandler) UpdateAccountBehaviorSettings(r api.AuthenticatedJSON[dto.AccountBehaviorSettingsUpdateRequest]) (api.Response, error) {
	ctx := r.Context()
	userID := r.UserID()
	if r.Body.RuntimeDebugEnabled == nil {
		settings, err := handler.workspaceService.AccountBehaviorSettingsForUser(ctx, userID)
		if err != nil {
			return api.Response{}, err
		}
		return api.OK(accountBehaviorSettingsResponse(settings)), nil
	}
	update := workspaceservice.AccountBehaviorSettingsUpdate{
		RuntimeDebugEnabled: r.Body.RuntimeDebugEnabled,
	}
	settings, err := handler.workspaceService.UpdateAccountBehaviorSettingsForUser(ctx, userID, update)
	if err != nil {
		return api.Response{}, err
	}
	if err := handler.syncAccountBehaviorSettingsMachineCatalogs(ctx, userID); err != nil {
		return api.Response{}, err
	}
	return api.OK(accountBehaviorSettingsResponse(settings)), nil
}

func accountBehaviorSettingsResponse(settings workspaceservice.AccountBehaviorSettings) dto.AccountBehaviorSettingsResponse {
	return dto.AccountBehaviorSettingsResponse{
		AccountID:                   settings.UserID,
		VisibleReplyContractEnabled: true, // reply enhancement is always-on; legacy field stays true
		RuntimeDebugEnabled:         settings.RuntimeDebugEnabled,
	}
}

func (handler *WorkspaceHandler) syncAccountBehaviorSettingsMachineCatalogs(ctx context.Context, userID string) error {
	if handler == nil || handler.machineRuntime == nil || handler.workspaceService == nil {
		return nil
	}
	workspaceIDs, err := managedWorkspaceIDs(ctx, handler.workspaceService, userID)
	if err != nil {
		return err
	}
	syncedMachineIDs := make(map[string]struct{})
	for _, workspaceID := range workspaceIDs {
		machines, err := handler.workspaceService.ListOwnMachinesForWorkspace(ctx, userID, workspaceID)
		if err != nil {
			return err
		}
		for _, machine := range machines {
			machineID := strings.TrimSpace(machine.ID)
			if machineID == "" {
				continue
			}
			if _, ok := syncedMachineIDs[machineID]; ok {
				continue
			}
			syncedMachineIDs[machineID] = struct{}{}
			if err := syncMachineCatalogIgnoringOffline(ctx, handler.machineRuntime, machineID); err != nil {
				return err
			}
		}
	}
	return nil
}
