package workspace

import (
	"context"
	"fmt"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/store/agent"
	"github.com/tidbcloud/AgentTeam/internal/store/app"
	"github.com/tidbcloud/AgentTeam/internal/store/app/runtime"
	"github.com/tidbcloud/AgentTeam/internal/store/identity"
	"github.com/tidbcloud/AgentTeam/internal/store/workspace"
)

type WorkspaceStore interface {
	ListUserWorkspaces(ctx context.Context, userID string) ([]workspace.Summary, error)
	GetWorkspaceForUser(ctx context.Context, userID string, workspaceID string) (workspace.Summary, error)
	GetChannelSidebarPreferenceForUser(ctx context.Context, userID string, workspaceID string) (workspace.ChannelSidebarPreference, bool, error)
	PutChannelSidebarPreferenceForUser(ctx context.Context, userID string, workspaceID string, state workspace.ChannelSidebarPreferenceState, expectedRevision *int64) (workspace.ChannelSidebarPreference, error)
	UpdateWorkspaceNameForUser(ctx context.Context, userID string, workspaceID string, name string) (workspace.Summary, error)
	CreateWorkspaceInvite(ctx context.Context, inviterUserID string, workspaceID string, email string, tokenHash string, expiresAt time.Time) (string, error)
	CreateWorkspaceMobileInvite(ctx context.Context, inviterUserID string, workspaceID string, mobile string, tokenHash string, expiresAt time.Time) (string, error)
}

type WorkspaceAdminStore interface {
	ListWorkspaceMembersForUser(ctx context.Context, userID string, workspaceID string, options workspace.WorkspaceMemberListOptions) (workspace.WorkspaceMemberPage, error)
	GetWorkspaceMemberForUser(ctx context.Context, actorUserID string, workspaceID string, memberID string) (workspace.WorkspaceMember, error)
	LookupWorkspaceMembersForUser(ctx context.Context, actorUserID string, workspaceID string, options workspace.WorkspaceMemberLookupOptions) ([]workspace.WorkspaceMember, error)
	RequireWorkspaceMemberManagementForUser(ctx context.Context, actorUserID string, workspaceID string) error
	WorkspaceMemberOffboardingImpactForUser(ctx context.Context, actorUserID string, workspaceID string, targetUserID string) (workspace.WorkspaceMemberOffboardingImpact, error)
	RemoveWorkspaceMemberForUser(ctx context.Context, actorUserID string, workspaceID string, targetUserID string) ([]string, error)
	UpdateWorkspaceMemberRoleForUser(ctx context.Context, actorUserID string, workspaceID string, targetUserID string, role string) error
	ListWorkspaceInvitesForUser(ctx context.Context, userID string, workspaceID string) ([]workspace.WorkspaceInvite, error)
	RevokeWorkspaceInviteForUser(ctx context.Context, userID string, workspaceID string, inviteID string) error
	LoadWorkspaceUsageForUser(ctx context.Context, userID string, workspaceID string) (workspace.WorkspaceUsage, error)
}

type MachineStore interface {
	CreateMachineForWorkspace(ctx context.Context, userID string, workspaceID string, name string) (app.Machine, string, error)
	AccountBehaviorSettingsForUser(ctx context.Context, userID string) (agent.AccountBehaviorSettings, error)
	ListOwnMachinesForWorkspace(ctx context.Context, userID string, workspaceID string) ([]app.Machine, error)
	ListOwnMachinesForWorkspacePage(ctx context.Context, userID string, workspaceID string, options app.MachineListOptions) (app.MachineListPage, error)
	ListAllMachinesForWorkspace(ctx context.Context, userID string, workspaceID string) ([]app.Machine, error)
	ListAllMachinesForWorkspacePage(ctx context.Context, userID string, workspaceID string, options app.MachineListOptions) (app.MachineListPage, error)
	RenameMachineForWorkspace(ctx context.Context, userID string, workspaceID string, machineID string, name string) error
	DeleteMachineForWorkspace(ctx context.Context, userID string, workspaceID string, machineID string) error
	EvictMachineForWorkspace(ctx context.Context, userID string, workspaceID string, machineID string) ([]string, error)
	ListAgentsForWorkspace(ctx context.Context, userID string, workspaceID string) ([]agent.AppAgent, error)
	ListAgentsForMachineWorkspace(ctx context.Context, userID string, workspaceID string, machineID string) ([]agent.AppAgent, error)
	UpdateAccountBehaviorSettingsForUser(ctx context.Context, userID string, update agent.AccountBehaviorSettingsUpdate) (agent.AccountBehaviorSettings, error)
}

type MachineTokenStore interface {
	CreateMachineConnectToken(ctx context.Context, userID string, workspaceID string, machineID string, serverURL string) (string, error)
	ExchangeMachineConnectToken(ctx context.Context, token string) (MachineConnectExchangeResult, error)
	ResolveMachineAuth(ctx context.Context, apiKey string, serverURL string) (MachineConnectExchangeResult, error)
	RequireMachineManagementForUser(ctx context.Context, userID string, workspaceID string, machineID string) error
	RotateMachineKeyForUser(ctx context.Context, userID string, workspaceID string, machineID string) (string, error)
}

// MiniappPairingStore is the issue-side surface for miniapp pairing codes. The
// exchange side lives in the auth layer because it mints a fresh user session.
type MiniappPairingStore interface {
	CreateMiniappPairingCode(ctx context.Context, userID string, workspaceID string, serverURL string) (MiniappPairingIssueResult, error)
}

// MiniappPairingIssueResult mirrors identity.MiniappPairingIssueResult.
type MiniappPairingIssueResult = identity.MiniappPairingIssueResult

type Dependencies struct {
	Workspaces      WorkspaceStore
	WorkspaceAdmin  WorkspaceAdminStore
	Machines        MachineStore
	MachineTokens   MachineTokenStore
	MiniappPairings MiniappPairingStore
}

type Service struct {
	workspaces      WorkspaceStore
	workspaceAdmin  WorkspaceAdminStore
	machines        MachineStore
	machineTokens   MachineTokenStore
	miniappPairings MiniappPairingStore
}

func New(deps Dependencies) *Service {
	return &Service{
		workspaces:      deps.Workspaces,
		workspaceAdmin:  deps.WorkspaceAdmin,
		machines:        deps.Machines,
		machineTokens:   deps.MachineTokens,
		miniappPairings: deps.MiniappPairings,
	}
}

type RuntimeDefaults struct {
	Model           string
	ReasoningEffort string
}

const (
	DefaultMachineListLimit = app.DefaultMachineListLimit
	MaxMachineListLimit     = app.MaxMachineListLimit
)

type MachineListOptions struct {
	Limit  int
	Cursor string
}

type MachineListPage struct {
	Machines   []Machine
	HasMore    bool
	NextCursor string
}

type Machine struct {
	ID                       string
	Name                     string
	Status                   string
	APIKeyPrefix             string
	CanManage                bool
	Hostname                 string
	OS                       string
	DaemonVersion            string
	Runtimes                 []string
	RuntimeDefaults          map[string]RuntimeDefaults
	CreatedAt                time.Time
	CreatedByUserID          string
	DirectoryBrowseSupported bool
	DashboardSupported       bool
}

type MachineConnectExchangeResult = agent.MachineConnectExchangeResult

type AccountBehaviorSettings = agent.AccountBehaviorSettings

type AccountBehaviorSettingsUpdate = agent.AccountBehaviorSettingsUpdate

type ChannelSidebarSection = workspace.ChannelSidebarSection

type ChannelSidebarPreferenceState = workspace.ChannelSidebarPreferenceState

type ChannelSidebarPreference = workspace.ChannelSidebarPreference

func (service *Service) ListUserWorkspaces(ctx context.Context, userID string) ([]workspace.Summary, error) {
	if service == nil || service.workspaces == nil {
		return nil, fmt.Errorf("workspaceflow workspace store is not configured")
	}
	return service.workspaces.ListUserWorkspaces(ctx, userID)
}

func (service *Service) GetWorkspaceForUser(ctx context.Context, userID string, workspaceID string) (workspace.Summary, error) {
	if service == nil || service.workspaces == nil {
		return workspace.Summary{}, fmt.Errorf("workspaceflow workspace store is not configured")
	}
	return service.workspaces.GetWorkspaceForUser(ctx, userID, workspaceID)
}

func (service *Service) GetChannelSidebarPreferenceForUser(ctx context.Context, userID string, workspaceID string) (ChannelSidebarPreference, bool, error) {
	if service == nil || service.workspaces == nil {
		return ChannelSidebarPreference{}, false, fmt.Errorf("workspaceflow workspace store is not configured")
	}
	return service.workspaces.GetChannelSidebarPreferenceForUser(ctx, userID, workspaceID)
}

func (service *Service) PutChannelSidebarPreferenceForUser(ctx context.Context, userID string, workspaceID string, state ChannelSidebarPreferenceState, expectedRevision *int64) (ChannelSidebarPreference, error) {
	if service == nil || service.workspaces == nil {
		return ChannelSidebarPreference{}, fmt.Errorf("workspaceflow workspace store is not configured")
	}
	return service.workspaces.PutChannelSidebarPreferenceForUser(ctx, userID, workspaceID, state, expectedRevision)
}

func (service *Service) UpdateWorkspaceNameForUser(ctx context.Context, userID string, workspaceID string, name string) (workspace.Summary, error) {
	if service == nil || service.workspaces == nil {
		return workspace.Summary{}, fmt.Errorf("workspaceflow workspace store is not configured")
	}
	return service.workspaces.UpdateWorkspaceNameForUser(ctx, userID, workspaceID, name)
}

func (service *Service) CreateWorkspaceInvite(ctx context.Context, inviterUserID string, workspaceID string, email string, tokenHash string, expiresAt time.Time) (string, error) {
	if service == nil || service.workspaces == nil {
		return "", fmt.Errorf("workspaceflow workspace store is not configured")
	}
	return service.workspaces.CreateWorkspaceInvite(ctx, inviterUserID, workspaceID, email, tokenHash, expiresAt)
}

func (service *Service) CreateWorkspaceMobileInvite(ctx context.Context, inviterUserID string, workspaceID string, mobile string, tokenHash string, expiresAt time.Time) (string, error) {
	if service == nil || service.workspaces == nil {
		return "", fmt.Errorf("workspaceflow workspace store is not configured")
	}
	return service.workspaces.CreateWorkspaceMobileInvite(ctx, inviterUserID, workspaceID, mobile, tokenHash, expiresAt)
}

func (service *Service) ListWorkspaceMembersForUser(ctx context.Context, userID string, workspaceID string) ([]workspace.WorkspaceMember, error) {
	if service == nil || service.workspaceAdmin == nil {
		return nil, fmt.Errorf("workspaceflow workspace admin store is not configured")
	}
	page, err := service.workspaceAdmin.ListWorkspaceMembersForUser(ctx, userID, workspaceID, workspace.WorkspaceMemberListOptions{})
	if err != nil {
		return nil, err
	}
	return page.Members, nil
}

func (service *Service) ListWorkspaceMembersForUserPage(ctx context.Context, userID string, workspaceID string, options workspace.WorkspaceMemberListOptions) (workspace.WorkspaceMemberPage, error) {
	if service == nil || service.workspaceAdmin == nil {
		return workspace.WorkspaceMemberPage{}, fmt.Errorf("workspaceflow workspace admin store is not configured")
	}
	return service.workspaceAdmin.ListWorkspaceMembersForUser(ctx, userID, workspaceID, options)
}

func (service *Service) GetWorkspaceMemberForUser(ctx context.Context, actorUserID string, workspaceID string, memberID string) (workspace.WorkspaceMember, error) {
	if service == nil || service.workspaceAdmin == nil {
		return workspace.WorkspaceMember{}, fmt.Errorf("workspaceflow workspace admin store is not configured")
	}
	return service.workspaceAdmin.GetWorkspaceMemberForUser(ctx, actorUserID, workspaceID, memberID)
}

func (service *Service) LookupWorkspaceMembersForUser(ctx context.Context, actorUserID string, workspaceID string, options workspace.WorkspaceMemberLookupOptions) ([]workspace.WorkspaceMember, error) {
	if service == nil || service.workspaceAdmin == nil {
		return nil, fmt.Errorf("workspaceflow workspace admin store is not configured")
	}
	return service.workspaceAdmin.LookupWorkspaceMembersForUser(ctx, actorUserID, workspaceID, options)
}

func (service *Service) RequireWorkspaceMemberManagementForUser(ctx context.Context, actorUserID string, workspaceID string) error {
	if service == nil || service.workspaceAdmin == nil {
		return fmt.Errorf("workspaceflow workspace admin store is not configured")
	}
	return service.workspaceAdmin.RequireWorkspaceMemberManagementForUser(ctx, actorUserID, workspaceID)
}

func (service *Service) UpdateWorkspaceMemberRoleForUser(ctx context.Context, actorUserID string, workspaceID string, targetUserID string, role string) error {
	if service == nil || service.workspaceAdmin == nil {
		return fmt.Errorf("workspaceflow workspace admin store is not configured")
	}
	return service.workspaceAdmin.UpdateWorkspaceMemberRoleForUser(ctx, actorUserID, workspaceID, targetUserID, role)
}

func (service *Service) WorkspaceMemberOffboardingImpactForUser(ctx context.Context, actorUserID string, workspaceID string, targetUserID string) (workspace.WorkspaceMemberOffboardingImpact, error) {
	if service == nil || service.workspaceAdmin == nil {
		return workspace.WorkspaceMemberOffboardingImpact{}, fmt.Errorf("workspaceflow workspace admin store is not configured")
	}
	return service.workspaceAdmin.WorkspaceMemberOffboardingImpactForUser(ctx, actorUserID, workspaceID, targetUserID)
}

func (service *Service) RemoveWorkspaceMemberForUser(ctx context.Context, actorUserID string, workspaceID string, targetUserID string) ([]string, error) {
	if service == nil || service.workspaceAdmin == nil {
		return nil, fmt.Errorf("workspaceflow workspace admin store is not configured")
	}
	return service.workspaceAdmin.RemoveWorkspaceMemberForUser(ctx, actorUserID, workspaceID, targetUserID)
}

func (service *Service) ListWorkspaceInvitesForUser(ctx context.Context, userID string, workspaceID string) ([]workspace.WorkspaceInvite, error) {
	if service == nil || service.workspaceAdmin == nil {
		return nil, fmt.Errorf("workspaceflow workspace admin store is not configured")
	}
	return service.workspaceAdmin.ListWorkspaceInvitesForUser(ctx, userID, workspaceID)
}

func (service *Service) RevokeWorkspaceInviteForUser(ctx context.Context, userID string, workspaceID string, inviteID string) error {
	if service == nil || service.workspaceAdmin == nil {
		return fmt.Errorf("workspaceflow workspace admin store is not configured")
	}
	return service.workspaceAdmin.RevokeWorkspaceInviteForUser(ctx, userID, workspaceID, inviteID)
}

func (service *Service) LoadWorkspaceUsageForUser(ctx context.Context, userID string, workspaceID string) (workspace.WorkspaceUsage, error) {
	if service == nil || service.workspaceAdmin == nil {
		return workspace.WorkspaceUsage{}, fmt.Errorf("workspaceflow workspace admin store is not configured")
	}
	return service.workspaceAdmin.LoadWorkspaceUsageForUser(ctx, userID, workspaceID)
}

func (service *Service) CreateMachineForWorkspace(ctx context.Context, userID string, workspaceID string, name string) (Machine, string, error) {
	if service == nil || service.machines == nil {
		return Machine{}, "", fmt.Errorf("workspaceflow machine store is not configured")
	}
	machine, apiKey, err := service.machines.CreateMachineForWorkspace(ctx, userID, workspaceID, name)
	if err != nil {
		return Machine{}, "", err
	}
	return fromStoreMachine(machine), apiKey, nil
}

func (service *Service) AccountBehaviorSettingsForUser(ctx context.Context, userID string) (AccountBehaviorSettings, error) {
	if service == nil || service.machines == nil {
		return AccountBehaviorSettings{}, fmt.Errorf("workspaceflow machine store is not configured")
	}
	return service.machines.AccountBehaviorSettingsForUser(ctx, userID)
}

func (service *Service) UpdateAccountBehaviorSettingsForUser(ctx context.Context, userID string, update agent.AccountBehaviorSettingsUpdate) (AccountBehaviorSettings, error) {
	if service == nil || service.machines == nil {
		return AccountBehaviorSettings{}, fmt.Errorf("workspaceflow machine store is not configured")
	}
	return service.machines.UpdateAccountBehaviorSettingsForUser(ctx, userID, update)
}

func (service *Service) ListOwnMachinesForWorkspace(ctx context.Context, userID string, workspaceID string) ([]Machine, error) {
	if service == nil || service.machines == nil {
		return nil, fmt.Errorf("workspaceflow machine store is not configured")
	}
	machines, err := service.machines.ListOwnMachinesForWorkspace(ctx, userID, workspaceID)
	if err != nil {
		return nil, err
	}
	return fromStoreMachines(machines), nil
}

func (service *Service) ListOwnMachinesForWorkspacePage(ctx context.Context, userID string, workspaceID string, options MachineListOptions) (MachineListPage, error) {
	if service == nil || service.machines == nil {
		return MachineListPage{}, fmt.Errorf("workspaceflow machine store is not configured")
	}
	page, err := service.machines.ListOwnMachinesForWorkspacePage(ctx, userID, workspaceID, toStoreMachineListOptions(options))
	if err != nil {
		return MachineListPage{}, err
	}
	return fromStoreMachineListPage(page), nil
}

func (service *Service) ListAllMachinesForWorkspace(ctx context.Context, userID string, workspaceID string) ([]Machine, error) {
	if service == nil || service.machines == nil {
		return nil, fmt.Errorf("workspaceflow machine store is not configured")
	}
	machines, err := service.machines.ListAllMachinesForWorkspace(ctx, userID, workspaceID)
	if err != nil {
		return nil, err
	}
	return fromStoreMachines(machines), nil
}

func (service *Service) ListAllMachinesForWorkspacePage(ctx context.Context, userID string, workspaceID string, options MachineListOptions) (MachineListPage, error) {
	if service == nil || service.machines == nil {
		return MachineListPage{}, fmt.Errorf("workspaceflow machine store is not configured")
	}
	page, err := service.machines.ListAllMachinesForWorkspacePage(ctx, userID, workspaceID, toStoreMachineListOptions(options))
	if err != nil {
		return MachineListPage{}, err
	}
	return fromStoreMachineListPage(page), nil
}

func (service *Service) RenameMachineForWorkspace(ctx context.Context, userID string, workspaceID string, machineID string, name string) error {
	if service == nil || service.machines == nil {
		return fmt.Errorf("workspaceflow machine store is not configured")
	}
	return service.machines.RenameMachineForWorkspace(ctx, userID, workspaceID, machineID, name)
}

func (service *Service) DeleteMachineForWorkspace(ctx context.Context, userID string, workspaceID string, machineID string) error {
	if service == nil || service.machines == nil {
		return fmt.Errorf("workspaceflow machine store is not configured")
	}
	return service.machines.DeleteMachineForWorkspace(ctx, userID, workspaceID, machineID)
}

func (service *Service) EvictMachineForWorkspace(ctx context.Context, userID string, workspaceID string, machineID string) ([]string, error) {
	if service == nil || service.machines == nil {
		return nil, fmt.Errorf("workspaceflow machine store is not configured")
	}
	return service.machines.EvictMachineForWorkspace(ctx, userID, workspaceID, machineID)
}

func (service *Service) ListAgentsForWorkspace(ctx context.Context, userID string, workspaceID string) ([]agent.AppAgent, error) {
	if service == nil || service.machines == nil {
		return nil, fmt.Errorf("workspaceflow machine store is not configured")
	}
	return service.machines.ListAgentsForWorkspace(ctx, userID, workspaceID)
}

func (service *Service) ListAgentsForMachineWorkspace(ctx context.Context, userID string, workspaceID string, machineID string) ([]agent.AppAgent, error) {
	if service == nil || service.machines == nil {
		return nil, fmt.Errorf("workspaceflow machine store is not configured")
	}
	return service.machines.ListAgentsForMachineWorkspace(ctx, userID, workspaceID, machineID)
}

func (service *Service) CreateMachineConnectToken(ctx context.Context, userID string, workspaceID string, machineID string, serverURL string) (string, error) {
	if service == nil || service.machineTokens == nil {
		return "", fmt.Errorf("workspaceflow machine token store is not configured")
	}
	return service.machineTokens.CreateMachineConnectToken(ctx, userID, workspaceID, machineID, serverURL)
}

func (service *Service) ExchangeMachineConnectToken(ctx context.Context, token string) (MachineConnectExchangeResult, error) {
	if service == nil || service.machineTokens == nil {
		return MachineConnectExchangeResult{}, fmt.Errorf("workspaceflow machine token store is not configured")
	}
	return service.machineTokens.ExchangeMachineConnectToken(ctx, token)
}

func (service *Service) ResolveMachineAuth(ctx context.Context, apiKey string, serverURL string) (MachineConnectExchangeResult, error) {
	if service == nil || service.machineTokens == nil {
		return MachineConnectExchangeResult{}, fmt.Errorf("workspaceflow machine token store is not configured")
	}
	return service.machineTokens.ResolveMachineAuth(ctx, apiKey, serverURL)
}

func (service *Service) RequireMachineManagementForUser(ctx context.Context, userID string, workspaceID string, machineID string) error {
	if service == nil || service.machineTokens == nil {
		return fmt.Errorf("workspaceflow machine token store is not configured")
	}
	return service.machineTokens.RequireMachineManagementForUser(ctx, userID, workspaceID, machineID)
}

func (service *Service) RotateMachineKeyForUser(ctx context.Context, userID string, workspaceID string, machineID string) (string, error) {
	if service == nil || service.machineTokens == nil {
		return "", fmt.Errorf("workspaceflow machine token store is not configured")
	}
	return service.machineTokens.RotateMachineKeyForUser(ctx, userID, workspaceID, machineID)
}

func (service *Service) CreateMiniappPairingCode(ctx context.Context, userID string, workspaceID string, serverURL string) (MiniappPairingIssueResult, error) {
	if service == nil || service.miniappPairings == nil {
		return MiniappPairingIssueResult{}, fmt.Errorf("workspaceflow miniapp pairing store is not configured")
	}
	return service.miniappPairings.CreateMiniappPairingCode(ctx, userID, workspaceID, serverURL)
}

func fromStoreMachine(machine app.Machine) Machine {
	return Machine{
		ID:              machine.ID,
		Name:            machine.Name,
		Status:          machine.Status,
		APIKeyPrefix:    machine.APIKeyPrefix,
		CanManage:       machine.CanManage,
		Hostname:        machine.Hostname,
		OS:              machine.OS,
		DaemonVersion:   machine.DaemonVersion,
		Runtimes:        cloneRuntimeNames(machine.Runtimes),
		RuntimeDefaults: fromStoreRuntimeDefaultsMap(machine.RuntimeDefaults),
		CreatedAt:       machine.CreatedAt,
		CreatedByUserID: machine.CreatedByUserID,
	}
}

func fromStoreMachines(machines []app.Machine) []Machine {
	if machines == nil {
		return nil
	}
	out := make([]Machine, 0, len(machines))
	for _, machine := range machines {
		out = append(out, fromStoreMachine(machine))
	}
	return out
}

func toStoreMachineListOptions(options MachineListOptions) app.MachineListOptions {
	return app.MachineListOptions{
		Limit:  options.Limit,
		Cursor: options.Cursor,
	}
}

func fromStoreMachineListPage(page app.MachineListPage) MachineListPage {
	return MachineListPage{
		Machines:   fromStoreMachines(page.Machines),
		HasMore:    page.HasMore,
		NextCursor: page.NextCursor,
	}
}

func fromStoreRuntimeDefaultsMap(defaults map[string]runtime.RuntimeDefaults) map[string]RuntimeDefaults {
	if len(defaults) == 0 {
		return map[string]RuntimeDefaults{}
	}
	out := make(map[string]RuntimeDefaults, len(defaults))
	for runtime, value := range defaults {
		out[runtime] = RuntimeDefaults{Model: value.Model, ReasoningEffort: value.ReasoningEffort}
	}
	return out
}

func cloneRuntimeNames(runtimes []string) []string {
	if len(runtimes) == 0 {
		return []string{}
	}
	return append([]string{}, runtimes...)
}
