package workspace

import (
	"context"
	"errors"
	"fmt"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/access"
	"github.com/tidbcloud/AgentTeam/internal/store/plan"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
)

var (
	ErrUnauthorized             = storeerr.ErrUnauthorized
	ErrForbidden                = storeerr.ErrForbidden
	ErrNotFound                 = storeerr.ErrNotFound
	ErrBadRequest               = storeerr.ErrBadRequest
	ErrGone                     = storeerr.ErrGone
	ErrSystemNotInitialized     = storeerr.ErrSystemNotInitialized
	ErrSystemAlreadyInitialized = storeerr.ErrSystemAlreadyInitialized
)

func badRequest(message string) error { return storeerr.BadRequest(message) }
func conflict(message string) error   { return storeerr.Conflict(message) }

type Options struct {
	RequireWorkspaceAccess                      func(context.Context, string, string) (access.HumanWorkspaceMember, error)
	RequireWorkspaceMemberManagement            func(context.Context, string, string) error
	EnsureAllChannelMembershipInTx              func(context.Context, coredb.Tx, string, string) error
	EnsureServiceAccountAudienceMembershipsInTx func(context.Context, coredb.Tx, string, string) error
	CheckWorkspaceQuotaInTx                     func(context.Context, coredb.Tx, string, plan.MetricKey, int64) error
}

type Store struct {
	db      coredb.DB
	options Options
}

func New(db coredb.DB, options Options) *Store {
	return &Store{db: db, options: options}
}

func (s *Store) Validate() error { return s.options.Validate() }

func (o Options) Validate() error {
	var missing []string
	if o.RequireWorkspaceAccess == nil {
		missing = append(missing, "RequireWorkspaceAccess")
	}
	if o.RequireWorkspaceMemberManagement == nil {
		missing = append(missing, "RequireWorkspaceMemberManagement")
	}
	if o.EnsureAllChannelMembershipInTx == nil {
		missing = append(missing, "EnsureAllChannelMembershipInTx")
	}
	if o.EnsureServiceAccountAudienceMembershipsInTx == nil {
		missing = append(missing, "EnsureServiceAccountAudienceMembershipsInTx")
	}
	if o.CheckWorkspaceQuotaInTx == nil {
		missing = append(missing, "CheckWorkspaceQuotaInTx")
	}
	if len(missing) > 0 {
		return fmt.Errorf("workspace store: missing dependencies: %s", strings.Join(missing, ", "))
	}
	return nil
}

func (store *Store) requireWorkspaceAccess(ctx context.Context, userID string, workspaceID string) (access.HumanWorkspaceMember, error) {
	if store == nil || store.options.RequireWorkspaceAccess == nil {
		return access.HumanWorkspaceMember{}, fmt.Errorf("workspace store access dependency is not configured")
	}
	return store.options.RequireWorkspaceAccess(ctx, userID, workspaceID)
}

func (store *Store) requireWorkspaceMemberManagement(ctx context.Context, userID string, workspaceID string) error {
	if store == nil || store.options.RequireWorkspaceMemberManagement == nil {
		return fmt.Errorf("workspace store member management dependency is not configured")
	}
	return store.options.RequireWorkspaceMemberManagement(ctx, userID, workspaceID)
}

// RequireWorkspaceMemberManagementForUser checks whether the user can manage
// workspace members.
func (store *Store) RequireWorkspaceMemberManagementForUser(ctx context.Context, userID string, workspaceID string) error {
	return store.requireWorkspaceMemberManagement(ctx, userID, workspaceID)
}

// RequireWorkspaceMemberRoleManagementForUser checks whether the user can
// change workspace member roles.
func (store *Store) RequireWorkspaceMemberRoleManagementForUser(ctx context.Context, userID string, workspaceID string) error {
	return store.requireWorkspaceRoleAction(ctx, userID, workspaceID, access.ActionManageMemberRoles)
}

// RequireWorkspaceRuntimeManagementForUser checks whether the user can manage
// runtime resources in the workspace.
func (store *Store) RequireWorkspaceRuntimeManagementForUser(ctx context.Context, userID string, workspaceID string) error {
	return store.requireWorkspaceRoleAction(ctx, userID, workspaceID, access.ActionManageRuntime)
}

func (store *Store) requireWorkspaceRoleAction(ctx context.Context, userID string, workspaceID string, action access.Action) error {
	if store == nil {
		return fmt.Errorf("workspace store authorization dependency is not configured")
	}
	_, err := access.LoadAuthorizedHumanWorkspaceMember(ctx, store.db, userID, workspaceID, access.HumanAction{
		Resource: access.ResourceWorkspace,
		Action:   action,
	})
	if errors.Is(err, access.ErrForbidden) {
		return ErrForbidden
	}
	if errors.Is(err, access.ErrNotFound) {
		return ErrNotFound
	}
	if err != nil {
		return fmt.Errorf("load authorized workspace member: %w", err)
	}
	return nil
}

func (store *Store) ensureAllChannelMembershipInTx(ctx context.Context, tx coredb.Tx, workspaceID string, memberID string) error {
	if store == nil || store.options.EnsureAllChannelMembershipInTx == nil {
		return fmt.Errorf("workspace store #all membership dependency is not configured")
	}
	return store.options.EnsureAllChannelMembershipInTx(ctx, tx, workspaceID, memberID)
}

func (store *Store) ensureServiceAccountAudienceMembershipsInTx(ctx context.Context, tx coredb.Tx, workspaceID string, memberID string) error {
	if store == nil || store.options.EnsureServiceAccountAudienceMembershipsInTx == nil {
		return fmt.Errorf("workspace store service-account audience dependency is not configured")
	}
	return store.options.EnsureServiceAccountAudienceMembershipsInTx(ctx, tx, workspaceID, memberID)
}

func (store *Store) checkWorkspaceQuotaInTx(ctx context.Context, tx coredb.Tx, workspaceID string, metric plan.MetricKey, delta int64) error {
	if store == nil || store.options.CheckWorkspaceQuotaInTx == nil {
		return fmt.Errorf("workspace store quota checker is not configured")
	}
	return store.options.CheckWorkspaceQuotaInTx(ctx, tx, workspaceID, metric, delta)
}
