package agent

import (
	"context"
	"errors"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/service/approvalapp"
	storeagent "github.com/tidbcloud/AgentTeam/internal/store/agent"
	"github.com/tidbcloud/AgentTeam/internal/store/workspace"
)

// AgentSkillOwnerResolver returns the current Human owner of an authenticated
// Agent. The request body never supplies this authority.
type AgentSkillOwnerResolver interface {
	AgentManagedByUser(context.Context, string, string) (string, error)
}

// AgentSkillWorkspaceResolver verifies that the resolved Human owner remains
// an active member of the authenticated Agent's workspace.
type AgentSkillWorkspaceResolver interface {
	GetWorkspaceForUser(context.Context, string, string) (workspace.Summary, error)
}

func (handler *AgentSkillHandler) resolvePrincipal(
	ctx context.Context,
	authenticated storeagent.AuthenticatedAgent,
) (AgentSkillPrincipal, error) {
	if handler == nil || handler.owners == nil || handler.workspaces == nil || handler.skills == nil {
		return AgentSkillPrincipal{}, errors.New("Skill runtime is not configured")
	}
	principal, _, err := resolveAgentSkillAuthority(
		ctx, handler.owners, handler.workspaces, authenticated,
	)
	return principal, err
}

func resolveAgentSkillAuthority(
	ctx context.Context,
	owners AgentSkillOwnerResolver,
	workspaces AgentSkillWorkspaceResolver,
	authenticated storeagent.AuthenticatedAgent,
) (AgentSkillPrincipal, workspace.Summary, error) {
	if owners == nil || workspaces == nil {
		return AgentSkillPrincipal{}, workspace.Summary{},
			errors.New("Skill authority is not configured")
	}
	workspaceID := strings.TrimSpace(authenticated.WorkspaceID)
	subject, err := owners.AgentManagedByUser(
		ctx, workspaceID, strings.TrimSpace(authenticated.AgentID),
	)
	if err != nil {
		return AgentSkillPrincipal{}, workspace.Summary{}, err
	}
	subject = strings.TrimSpace(subject)
	if subject == "" {
		return AgentSkillPrincipal{}, workspace.Summary{},
			errors.New("Skill owner resolution returned an empty Human")
	}
	membership, err := workspaces.GetWorkspaceForUser(ctx, subject, workspaceID)
	if err != nil {
		return AgentSkillPrincipal{}, workspace.Summary{}, err
	}
	if strings.TrimSpace(membership.WorkspaceID) != workspaceID ||
		strings.TrimSpace(membership.MemberUserID) != subject ||
		strings.TrimSpace(membership.MemberID) == "" {
		return AgentSkillPrincipal{}, workspace.Summary{}, api.ErrForbidden
	}
	return AgentSkillPrincipal{
		WorkspaceID: workspaceID,
		Subject:     subject,
	}, membership, nil
}

// AgentSkillDeleteAuthority is the execute-time bridge used by the Approval
// subject. Every approved replay re-resolves the current Human owner and
// current manage permission before the existing Resource DELETE is called.
type AgentSkillDeleteAuthority struct {
	owners     AgentSkillOwnerResolver
	workspaces AgentSkillWorkspaceResolver
	skills     AgentSkillGateway
}

func NewAgentSkillDeleteAuthority(
	owners AgentSkillOwnerResolver,
	workspaces AgentSkillWorkspaceResolver,
	skills AgentSkillGateway,
) *AgentSkillDeleteAuthority {
	return &AgentSkillDeleteAuthority{
		owners: owners, workspaces: workspaces, skills: skills,
	}
}

func (authority *AgentSkillDeleteAuthority) Prepare(
	ctx context.Context,
	command approvalapp.SkillDeleteAuthorityCommand,
) (approvalapp.SkillDeletePrepared, error) {
	principal, membership, err := authority.resolve(ctx, command)
	if err != nil {
		return approvalapp.SkillDeletePrepared{}, err
	}
	skill, err := authority.skills.Get(ctx, principal, command.SkillID)
	if err != nil {
		return approvalapp.SkillDeletePrepared{}, err
	}
	if strings.TrimSpace(skill.ID) != strings.TrimSpace(command.SkillID) ||
		!skill.CanManage {
		return approvalapp.SkillDeletePrepared{}, api.ErrForbidden
	}
	return approvalapp.SkillDeletePrepared{
		SkillID:          skill.ID,
		SkillName:        skill.Name,
		ApproverMemberID: membership.MemberID,
	}, nil
}

func (authority *AgentSkillDeleteAuthority) Execute(
	ctx context.Context,
	command approvalapp.SkillDeleteAuthorityCommand,
) error {
	principal, _, err := authority.resolve(ctx, command)
	if err != nil {
		return err
	}
	skill, err := authority.skills.Get(ctx, principal, command.SkillID)
	if errors.Is(err, api.ErrNotFound) {
		return nil
	}
	if err != nil {
		return err
	}
	if strings.TrimSpace(skill.ID) != strings.TrimSpace(command.SkillID) ||
		!skill.CanManage {
		return api.ErrForbidden
	}
	err = authority.skills.Delete(ctx, principal, command.SkillID)
	if errors.Is(err, api.ErrNotFound) {
		return nil
	}
	return err
}

func (authority *AgentSkillDeleteAuthority) resolve(
	ctx context.Context,
	command approvalapp.SkillDeleteAuthorityCommand,
) (AgentSkillPrincipal, workspace.Summary, error) {
	if authority == nil || authority.owners == nil ||
		authority.workspaces == nil || authority.skills == nil {
		return AgentSkillPrincipal{}, workspace.Summary{},
			errors.New("Skill delete authority is not configured")
	}
	principal := command.Principal
	if strings.TrimSpace(principal.ActorType) != "agent" ||
		strings.TrimSpace(principal.WorkspaceID) == "" ||
		strings.TrimSpace(principal.PrincipalID) == "" {
		return AgentSkillPrincipal{}, workspace.Summary{}, api.ErrForbidden
	}
	return resolveAgentSkillAuthority(
		ctx, authority.owners, authority.workspaces,
		storeagent.AuthenticatedAgent{
			WorkspaceID: principal.WorkspaceID,
			AgentID:     principal.PrincipalID,
			MemberID:    principal.MemberID,
			MachineID:   principal.MachineID,
		},
	)
}
