package organization

import (
	"testing"
	"time"

	"github.com/stretchr/testify/require"
)

func TestHostedModelSettingsDefaultsAndPatch(t *testing.T) {
	t.Parallel()

	runtime := startOrganizationServiceRuntime(t)
	owner := bootstrapOrganizationAdmin(t, runtime, "owner@example.com", "owner")

	organization, err := runtime.service.CreateOrganizationForUser(runtime.ctx, owner, "Hosted Org", "hosted-org")
	require.NoError(t, err)

	settings, err := runtime.service.GetOrganizationHostedModelSettingsForUser(runtime.ctx, owner.ID, organization.ID)
	require.NoError(t, err)
	require.Equal(t, organizationHostedPickerVisibilityShowDisabled, settings.PickerVisibilityPolicy)
	require.True(t, settings.AutoAuthorizationEnabled)
	require.Equal(t, organizationHostedAuthorizationModeAllActive, settings.AutoAuthorizationMode)
	require.Empty(t, settings.AutoAuthorizationModelRefs)

	visibility := organizationHostedPickerVisibilityHideDisabled
	enabled := true
	mode := organizationHostedAuthorizationModeSelected
	modelRefs := []string{" agentteam/deepseek-v4-pro ", "", "agentteam/deepseek-v4-flash", "agentteam/deepseek-v4-pro"}
	settings, err = runtime.service.PatchOrganizationHostedModelSettingsForUser(runtime.ctx, owner.ID, organization.ID, UpdateOrganizationHostedModelSettingsParams{
		PickerVisibilityPolicy:     &visibility,
		AutoAuthorizationEnabled:   &enabled,
		AutoAuthorizationMode:      &mode,
		AutoAuthorizationModelRefs: &modelRefs,
	})
	require.NoError(t, err)
	require.Equal(t, organizationHostedPickerVisibilityHideDisabled, settings.PickerVisibilityPolicy)
	require.True(t, settings.AutoAuthorizationEnabled)
	require.Equal(t, organizationHostedAuthorizationModeSelected, settings.AutoAuthorizationMode)
	require.Equal(t, []string{"agentteam/deepseek-v4-pro", "agentteam/deepseek-v4-flash"}, settings.AutoAuthorizationModelRefs)

	mode = organizationHostedAuthorizationModeAllActive
	settings, err = runtime.service.PatchOrganizationHostedModelSettingsForUser(runtime.ctx, owner.ID, organization.ID, UpdateOrganizationHostedModelSettingsParams{
		AutoAuthorizationMode: &mode,
	})
	require.NoError(t, err)
	require.Equal(t, organizationHostedAuthorizationModeAllActive, settings.AutoAuthorizationMode)
	require.Empty(t, settings.AutoAuthorizationModelRefs)

	mode = organizationHostedAuthorizationModeSelected
	emptyRefs := []string{}
	_, err = runtime.service.PatchOrganizationHostedModelSettingsForUser(runtime.ctx, owner.ID, organization.ID, UpdateOrganizationHostedModelSettingsParams{
		AutoAuthorizationMode:      &mode,
		AutoAuthorizationModelRefs: &emptyRefs,
	})
	require.ErrorIs(t, err, ErrBadRequest)
}

func TestWorkspaceHostedModelAuthorizationLifecycle(t *testing.T) {
	t.Parallel()

	runtime := startOrganizationServiceRuntime(t)
	owner := bootstrapOrganizationAdmin(t, runtime, "owner@example.com", "owner")

	organization, err := runtime.service.CreateOrganizationForUser(runtime.ctx, owner, "Workspace Hosted Org", "workspace-hosted-org")
	require.NoError(t, err)
	workspace := requireDefaultOrganizationWorkspace(t, runtime, owner.ID, organization.ID)

	authorization, err := runtime.service.GetOrganizationWorkspaceHostedModelAuthorizationForUser(runtime.ctx, owner.ID, organization.ID, workspace.ID)
	require.NoError(t, err)
	require.Equal(t, organizationHostedAuthorizationModeAllActive, authorization.AuthorizationMode)
	require.Equal(t, organizationHostedAuthorizationStatusActive, authorization.Status)
	require.True(t, authorization.Configured)
	require.Equal(t, owner.ID, authorization.AuthorizedByUserID)
	require.Empty(t, authorization.SelectedModelRefs)

	authorization, err = runtime.service.UpsertOrganizationWorkspaceHostedModelAuthorizationForUser(runtime.ctx, owner.ID, organization.ID, workspace.ID, UpsertOrganizationWorkspaceHostedModelAuthorizationParams{
		AuthorizationMode: organizationHostedAuthorizationModeAllActive,
	})
	require.NoError(t, err)
	require.Equal(t, organizationHostedAuthorizationModeAllActive, authorization.AuthorizationMode)
	require.Equal(t, organizationHostedAuthorizationStatusActive, authorization.Status)
	require.True(t, authorization.Configured)
	require.Equal(t, owner.ID, authorization.AuthorizedByUserID)
	require.Zero(t, authorization.AffectedHostedAgentCount)

	authorization, err = runtime.service.UpsertOrganizationWorkspaceHostedModelAuthorizationForUser(runtime.ctx, owner.ID, organization.ID, workspace.ID, UpsertOrganizationWorkspaceHostedModelAuthorizationParams{
		AuthorizationMode: organizationHostedAuthorizationModeSelected,
		SelectedModelRefs: []string{" agentteam/deepseek-v4-pro ", "agentteam/deepseek-v4-pro", "agentteam/deepseek-v4-flash"},
	})
	require.NoError(t, err)
	require.Equal(t, organizationHostedAuthorizationModeSelected, authorization.AuthorizationMode)
	require.Equal(t, []string{"agentteam/deepseek-v4-pro", "agentteam/deepseek-v4-flash"}, authorization.SelectedModelRefs)

	authorization, err = runtime.service.UpsertOrganizationWorkspaceHostedModelAuthorizationForUser(runtime.ctx, owner.ID, organization.ID, workspace.ID, UpsertOrganizationWorkspaceHostedModelAuthorizationParams{
		AuthorizationMode: organizationHostedAuthorizationModeUnauthorized,
	})
	require.NoError(t, err)
	require.Equal(t, organizationHostedAuthorizationModeUnauthorized, authorization.AuthorizationMode)
	require.Equal(t, organizationHostedAuthorizationStatusInactive, authorization.Status)
	require.True(t, authorization.Configured)
	require.Empty(t, authorization.SelectedModelRefs)
}

func TestWorkspaceHostedModelAuthorizationPreviewDoesNotPersist(t *testing.T) {
	t.Parallel()

	runtime := startOrganizationServiceRuntime(t)
	owner := bootstrapOrganizationAdmin(t, runtime, "owner@example.com", "owner")

	organization, err := runtime.service.CreateOrganizationForUser(runtime.ctx, owner, "Hosted Preview Org", "hosted-preview-org")
	require.NoError(t, err)
	workspace := requireDefaultOrganizationWorkspace(t, runtime, owner.ID, organization.ID)

	authorization, err := runtime.service.UpsertOrganizationWorkspaceHostedModelAuthorizationForUser(runtime.ctx, owner.ID, organization.ID, workspace.ID, UpsertOrganizationWorkspaceHostedModelAuthorizationParams{
		AuthorizationMode: organizationHostedAuthorizationModeAllActive,
	})
	require.NoError(t, err)
	require.Equal(t, organizationHostedAuthorizationModeAllActive, authorization.AuthorizationMode)

	preview, err := runtime.service.PreviewOrganizationWorkspaceHostedModelAuthorizationForUser(runtime.ctx, owner.ID, organization.ID, workspace.ID, UpsertOrganizationWorkspaceHostedModelAuthorizationParams{
		AuthorizationMode: organizationHostedAuthorizationModeUnauthorized,
	})
	require.NoError(t, err)
	require.Equal(t, organizationHostedAuthorizationModeUnauthorized, preview.AuthorizationMode)
	require.Equal(t, organizationHostedAuthorizationStatusInactive, preview.Status)
	require.True(t, preview.Configured)
	require.Zero(t, preview.AffectedHostedAgentCount)

	authorization, err = runtime.service.GetOrganizationWorkspaceHostedModelAuthorizationForUser(runtime.ctx, owner.ID, organization.ID, workspace.ID)
	require.NoError(t, err)
	require.Equal(t, organizationHostedAuthorizationModeAllActive, authorization.AuthorizationMode)
	require.Equal(t, organizationHostedAuthorizationStatusActive, authorization.Status)
	require.True(t, authorization.Configured)
}

func TestWorkspaceHostedModelAuthorizationPreviewCountsDirectHostedAgents(t *testing.T) {
	t.Parallel()

	runtime := startOrganizationServiceRuntime(t)
	owner := bootstrapOrganizationAdmin(t, runtime, "owner@example.com", "owner")

	organization, err := runtime.service.CreateOrganizationForUser(runtime.ctx, owner, "Hosted Direct Agent Org", "hosted-direct-agent-org")
	require.NoError(t, err)
	workspace := requireDefaultOrganizationWorkspace(t, runtime, owner.ID, organization.ID)

	_, err = runtime.service.UpsertOrganizationWorkspaceHostedModelAuthorizationForUser(runtime.ctx, owner.ID, organization.ID, workspace.ID, UpsertOrganizationWorkspaceHostedModelAuthorizationParams{
		AuthorizationMode: organizationHostedAuthorizationModeAllActive,
	})
	require.NoError(t, err)

	now := time.Now().UTC()
	_, err = runtime.pool.Exec(runtime.ctx, `
INSERT INTO machines (id, workspace_id, created_by_user_id, name, api_key, created_at, updated_at)
VALUES ($1, $2, $3, 'Hosted preview machine', 'hosted-preview-machine-key', $4, $4)
`, "mac_hosted_preview", workspace.ID, owner.ID, now)
	require.NoError(t, err)
	_, err = runtime.pool.Exec(runtime.ctx, `
INSERT INTO members (id, workspace_id, type, display_name, created_at, updated_at)
VALUES ($1, $2, 'agent', 'Hosted Preview Agent', $3, $3)
`, "mem_hosted_preview_agent", workspace.ID, now)
	require.NoError(t, err)
	_, err = runtime.pool.Exec(runtime.ctx, `
INSERT INTO agents (
  id, workspace_id, machine_id, member_id, name, runtime, model, llm_mode,
  description, status, activity, activity_detail, created_at, updated_at
) VALUES (
  $1, $2, $3, $4, 'hosted-preview-agent', 'claude', 'agentteam/deepseek-v4-pro', $5,
  '', 'inactive', 'offline', '', $6, $6
)
`, "agt_hosted_preview", workspace.ID, "mac_hosted_preview", "mem_hosted_preview_agent", organizationHostedAgentLLMMode, now)
	require.NoError(t, err)

	preview, err := runtime.service.PreviewOrganizationWorkspaceHostedModelAuthorizationForUser(runtime.ctx, owner.ID, organization.ID, workspace.ID, UpsertOrganizationWorkspaceHostedModelAuthorizationParams{
		AuthorizationMode: organizationHostedAuthorizationModeUnauthorized,
	})
	require.NoError(t, err)
	require.Equal(t, 1, preview.AffectedHostedAgentCount)
}

func TestHostedModelSelectionProjectionForWorkspace(t *testing.T) {
	t.Parallel()

	runtime := startOrganizationServiceRuntime(t)
	owner := bootstrapOrganizationAdmin(t, runtime, "owner@example.com", "owner")

	organization, err := runtime.service.CreateOrganizationForUser(runtime.ctx, owner, "Hosted Selection Org", "hosted-selection-org")
	require.NoError(t, err)
	workspace := requireDefaultOrganizationWorkspace(t, runtime, owner.ID, organization.ID)

	selection, err := runtime.service.GetHostedModelSelectionForWorkspace(runtime.ctx, organization.ID, workspace.ID)
	require.NoError(t, err)
	require.Equal(t, organizationHostedPickerVisibilityShowDisabled, selection.PickerVisibilityPolicy)
	require.Equal(t, organizationHostedAuthorizationModeAllActive, selection.AuthorizationMode)
	require.Equal(t, organizationHostedAuthorizationStatusActive, selection.AuthorizationStatus)
	require.True(t, selection.AuthorizationConfigured)

	visibility := organizationHostedPickerVisibilityHideDisabled
	_, err = runtime.service.PatchOrganizationHostedModelSettingsForUser(runtime.ctx, owner.ID, organization.ID, UpdateOrganizationHostedModelSettingsParams{
		PickerVisibilityPolicy: &visibility,
	})
	require.NoError(t, err)
	_, err = runtime.service.UpsertOrganizationWorkspaceHostedModelAuthorizationForUser(runtime.ctx, owner.ID, organization.ID, workspace.ID, UpsertOrganizationWorkspaceHostedModelAuthorizationParams{
		AuthorizationMode: organizationHostedAuthorizationModeSelected,
		SelectedModelRefs: []string{"agentteam/deepseek-v4-pro"},
	})
	require.NoError(t, err)

	selection, err = runtime.service.GetHostedModelSelectionForWorkspace(runtime.ctx, organization.ID, workspace.ID)
	require.NoError(t, err)
	require.Equal(t, organizationHostedPickerVisibilityHideDisabled, selection.PickerVisibilityPolicy)
	require.Equal(t, organizationHostedAuthorizationModeSelected, selection.AuthorizationMode)
	require.Equal(t, []string{"agentteam/deepseek-v4-pro"}, selection.SelectedModelRefs)
	require.Equal(t, organizationHostedAuthorizationStatusActive, selection.AuthorizationStatus)
	require.True(t, selection.AuthorizationConfigured)
}

func TestHostedAutoAuthorizationAppliesToNewWorkspaces(t *testing.T) {
	t.Parallel()

	runtime := startOrganizationServiceRuntime(t)
	owner := bootstrapOrganizationAdmin(t, runtime, "owner@example.com", "owner")

	organization, err := runtime.service.CreateOrganizationForUser(runtime.ctx, owner, "Hosted Auto Org", "hosted-auto-org")
	require.NoError(t, err)

	enabled := true
	mode := organizationHostedAuthorizationModeSelected
	modelRefs := []string{"agentteam/deepseek-v4-pro"}
	_, err = runtime.service.PatchOrganizationHostedModelSettingsForUser(runtime.ctx, owner.ID, organization.ID, UpdateOrganizationHostedModelSettingsParams{
		AutoAuthorizationEnabled:   &enabled,
		AutoAuthorizationMode:      &mode,
		AutoAuthorizationModelRefs: &modelRefs,
	})
	require.NoError(t, err)

	workspace, err := runtime.service.CreateOrganizationWorkspaceForUser(runtime.ctx, owner.ID, organization.ID, "Project One", "project-one")
	require.NoError(t, err)

	authorization, err := runtime.service.GetOrganizationWorkspaceHostedModelAuthorizationForUser(runtime.ctx, owner.ID, organization.ID, workspace.ID)
	require.NoError(t, err)
	require.Equal(t, organizationHostedAuthorizationModeSelected, authorization.AuthorizationMode)
	require.Equal(t, organizationHostedAuthorizationStatusActive, authorization.Status)
	require.True(t, authorization.Configured)
	require.Equal(t, []string{"agentteam/deepseek-v4-pro"}, authorization.SelectedModelRefs)
}

func TestDefaultHostedAutoAuthorizationAllowsFutureWorkspaces(t *testing.T) {
	t.Parallel()

	runtime := startOrganizationServiceRuntime(t)
	owner := bootstrapOrganizationAdmin(t, runtime, "owner@example.com", "owner")

	organization, err := runtime.service.CreateOrganizationForUser(runtime.ctx, owner, "Hosted Default Org", "hosted-default-org")
	require.NoError(t, err)
	defaultWorkspace := requireDefaultOrganizationWorkspace(t, runtime, owner.ID, organization.ID)

	settings, err := runtime.service.GetOrganizationHostedModelSettingsForUser(runtime.ctx, owner.ID, organization.ID)
	require.NoError(t, err)
	require.True(t, settings.AutoAuthorizationEnabled)
	require.Equal(t, organizationHostedAuthorizationModeAllActive, settings.AutoAuthorizationMode)

	defaultAuthorization, err := runtime.service.GetOrganizationWorkspaceHostedModelAuthorizationForUser(runtime.ctx, owner.ID, organization.ID, defaultWorkspace.ID)
	require.NoError(t, err)
	require.Equal(t, organizationHostedAuthorizationModeAllActive, defaultAuthorization.AuthorizationMode)
	require.Equal(t, organizationHostedAuthorizationStatusActive, defaultAuthorization.Status)
	require.True(t, defaultAuthorization.Configured)

	workspace, err := runtime.service.CreateOrganizationWorkspaceForUser(runtime.ctx, owner.ID, organization.ID, "Project One", "project-one")
	require.NoError(t, err)

	authorization, err := runtime.service.GetOrganizationWorkspaceHostedModelAuthorizationForUser(runtime.ctx, owner.ID, organization.ID, workspace.ID)
	require.NoError(t, err)
	require.Equal(t, organizationHostedAuthorizationModeAllActive, authorization.AuthorizationMode)
	require.Equal(t, organizationHostedAuthorizationStatusActive, authorization.Status)
	require.True(t, authorization.Configured)
	require.Empty(t, authorization.SelectedModelRefs)
}

func TestHostedModelSelectionInheritsOrgDefaultWhenWorkspaceAuthorizationMissing(t *testing.T) {
	t.Parallel()

	runtime := startOrganizationServiceRuntime(t)
	owner := bootstrapOrganizationAdmin(t, runtime, "owner@example.com", "owner")

	organization, err := runtime.service.CreateOrganizationForUser(runtime.ctx, owner, "Hosted Inherit Org", "hosted-inherit-org")
	require.NoError(t, err)

	enabled := false
	_, err = runtime.service.PatchOrganizationHostedModelSettingsForUser(runtime.ctx, owner.ID, organization.ID, UpdateOrganizationHostedModelSettingsParams{
		AutoAuthorizationEnabled: &enabled,
	})
	require.NoError(t, err)
	workspace, err := runtime.service.CreateOrganizationWorkspaceForUser(runtime.ctx, owner.ID, organization.ID, "Project One", "project-one")
	require.NoError(t, err)

	selection, err := runtime.service.GetHostedModelSelectionForWorkspace(runtime.ctx, organization.ID, workspace.ID)
	require.NoError(t, err)
	require.Equal(t, organizationHostedAuthorizationModeUnauthorized, selection.AuthorizationMode)
	require.Equal(t, organizationHostedAuthorizationStatusInactive, selection.AuthorizationStatus)
	require.False(t, selection.AuthorizationConfigured)
	require.Empty(t, selection.SelectedModelRefs)

	enabled = true
	mode := organizationHostedAuthorizationModeSelected
	modelRefs := []string{"agentteam/deepseek-v4-pro"}
	_, err = runtime.service.PatchOrganizationHostedModelSettingsForUser(runtime.ctx, owner.ID, organization.ID, UpdateOrganizationHostedModelSettingsParams{
		AutoAuthorizationEnabled:   &enabled,
		AutoAuthorizationMode:      &mode,
		AutoAuthorizationModelRefs: &modelRefs,
	})
	require.NoError(t, err)

	selection, err = runtime.service.GetHostedModelSelectionForWorkspace(runtime.ctx, organization.ID, workspace.ID)
	require.NoError(t, err)
	require.Equal(t, organizationHostedAuthorizationModeSelected, selection.AuthorizationMode)
	require.Equal(t, organizationHostedAuthorizationStatusActive, selection.AuthorizationStatus)
	require.False(t, selection.AuthorizationConfigured)
	require.Equal(t, []string{"agentteam/deepseek-v4-pro"}, selection.SelectedModelRefs)

	authorization, err := runtime.service.GetOrganizationWorkspaceHostedModelAuthorizationForUser(runtime.ctx, owner.ID, organization.ID, workspace.ID)
	require.NoError(t, err)
	require.Equal(t, organizationHostedAuthorizationModeSelected, authorization.AuthorizationMode)
	require.Equal(t, organizationHostedAuthorizationStatusActive, authorization.Status)
	require.False(t, authorization.Configured)
	require.Equal(t, []string{"agentteam/deepseek-v4-pro"}, authorization.SelectedModelRefs)
}

func TestEnsureDefaultWorkspaceForUserBootstrapsHostedAuthorization(t *testing.T) {
	t.Parallel()

	runtime := startOrganizationServiceRuntime(t)
	_ = bootstrapOrganizationAdmin(t, runtime, "admin@example.com", "admin")
	user := createVerifiedOrganizationUser(t, runtime, "user@example.com", "user")

	require.NoError(t, runtime.service.EnsureDefaultWorkspaceForUser(runtime.ctx, user))

	organizations, err := runtime.service.ListOrganizationsForUser(runtime.ctx, user.ID)
	require.NoError(t, err)
	require.Len(t, organizations, 1)
	defaultWorkspace := requireDefaultOrganizationWorkspace(t, runtime, user.ID, organizations[0].ID)

	authorization, err := runtime.service.GetOrganizationWorkspaceHostedModelAuthorizationForUser(runtime.ctx, user.ID, organizations[0].ID, defaultWorkspace.ID)
	require.NoError(t, err)
	require.Equal(t, organizationHostedAuthorizationModeAllActive, authorization.AuthorizationMode)
	require.Equal(t, organizationHostedAuthorizationStatusActive, authorization.Status)
	require.True(t, authorization.Configured)
	require.Equal(t, user.ID, authorization.AuthorizedByUserID)
}

func TestHostedModelAccessRequiresOrganizationAdmin(t *testing.T) {
	t.Parallel()

	runtime := startOrganizationServiceRuntime(t)
	owner := bootstrapOrganizationAdmin(t, runtime, "owner@example.com", "owner")
	outsider := createVerifiedOrganizationUser(t, runtime, "outsider@example.com", "outsider")

	organization, err := runtime.service.CreateOrganizationForUser(runtime.ctx, owner, "Hosted Guard Org", "hosted-guard-org")
	require.NoError(t, err)
	workspace := requireDefaultOrganizationWorkspace(t, runtime, owner.ID, organization.ID)

	_, err = runtime.service.GetOrganizationHostedModelSettingsForUser(runtime.ctx, outsider.ID, organization.ID)
	require.ErrorIs(t, err, ErrForbidden)

	_, err = runtime.service.UpsertOrganizationWorkspaceHostedModelAuthorizationForUser(runtime.ctx, outsider.ID, organization.ID, workspace.ID, UpsertOrganizationWorkspaceHostedModelAuthorizationParams{
		AuthorizationMode: organizationHostedAuthorizationModeAllActive,
	})
	require.ErrorIs(t, err, ErrForbidden)
}
