package access

import (
	"context"
	"fmt"
	"strings"

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

// AuthenticatedAgentPrincipal is the complete Agent identity authenticated
// before a caller-owned Core transaction begins.
type AuthenticatedAgentPrincipal struct {
	WorkspaceID string
	AgentID     string
	MemberID    string
	MachineID   string
}

// HumanManager is the exact Human authority resolved for an Agent principal.
type HumanManager struct {
	UserID   string
	MemberID string
}

// LockAndRevalidateAgentPrincipalManager locks and revalidates an authenticated
// Agent principal and its expected Human manager inside a caller-owned Core
// transaction. Callers must complete any exact replay check before invoking
// this helper.
//
// The lock order matches runtime/offboarding mutations: Human manager member,
// machine, Agent, then Agent member. In particular, the expected MachineID is
// authoritative input; this helper never derives a replacement from the
// Agent's current relationship.
func LockAndRevalidateAgentPrincipalManager(
	ctx context.Context,
	tx coredb.Tx,
	expected AuthenticatedAgentPrincipal,
	manager HumanManager,
) error {
	expected.WorkspaceID = strings.TrimSpace(expected.WorkspaceID)
	expected.AgentID = strings.TrimSpace(expected.AgentID)
	expected.MemberID = strings.TrimSpace(expected.MemberID)
	expected.MachineID = strings.TrimSpace(expected.MachineID)
	manager.UserID = strings.TrimSpace(manager.UserID)
	manager.MemberID = strings.TrimSpace(manager.MemberID)
	if tx == nil ||
		expected.WorkspaceID == "" ||
		expected.AgentID == "" ||
		expected.MemberID == "" ||
		expected.MachineID == "" ||
		manager.UserID == "" ||
		manager.MemberID == "" {
		return storeerr.ErrForbidden
	}

	var managerMemberID, managerWorkspaceID, managerType, managerUserID, managerStatus string
	if err := tx.QueryRow(ctx, `
SELECT id, workspace_id, type, COALESCE(user_id, ''), status
FROM members
WHERE workspace_id = $1 AND id = $2
FOR UPDATE
`, expected.WorkspaceID, manager.MemberID).Scan(
		&managerMemberID,
		&managerWorkspaceID,
		&managerType,
		&managerUserID,
		&managerStatus,
	); err != nil {
		return ownershipRevalidationError("lock expected Human manager member", err)
	}
	if managerMemberID != manager.MemberID ||
		managerWorkspaceID != expected.WorkspaceID ||
		managerType != "human" ||
		managerUserID != manager.UserID ||
		managerStatus != "active" {
		return storeerr.ErrForbidden
	}

	var machineID, machineWorkspaceID, machineCreatorUserID, machineStatus string
	if err := tx.QueryRow(ctx, `
SELECT id, workspace_id, created_by_user_id, status
FROM machines
WHERE workspace_id = $1 AND id = $2
FOR UPDATE
`, expected.WorkspaceID, expected.MachineID).Scan(
		&machineID,
		&machineWorkspaceID,
		&machineCreatorUserID,
		&machineStatus,
	); err != nil {
		return ownershipRevalidationError("lock expected Agent machine", err)
	}
	if machineID != expected.MachineID ||
		machineWorkspaceID != expected.WorkspaceID ||
		machineCreatorUserID != manager.UserID ||
		machineStatus != "active" {
		return storeerr.ErrForbidden
	}

	var agentID, agentWorkspaceID, agentMachineID, agentMemberID, agentStatus string
	if err := tx.QueryRow(ctx, `
SELECT id, workspace_id, machine_id, member_id, status
FROM agents
WHERE workspace_id = $1 AND id = $2
FOR UPDATE
`, expected.WorkspaceID, expected.AgentID).Scan(
		&agentID,
		&agentWorkspaceID,
		&agentMachineID,
		&agentMemberID,
		&agentStatus,
	); err != nil {
		return ownershipRevalidationError("lock expected Agent", err)
	}
	if agentID != expected.AgentID ||
		agentWorkspaceID != expected.WorkspaceID ||
		agentMachineID != expected.MachineID ||
		agentMemberID != expected.MemberID ||
		agentStatus == "archived" {
		return storeerr.ErrForbidden
	}

	var memberID, memberWorkspaceID, memberType, memberStatus string
	if err := tx.QueryRow(ctx, `
SELECT id, workspace_id, type, status
FROM members
WHERE workspace_id = $1 AND id = $2
FOR UPDATE
`, expected.WorkspaceID, expected.MemberID).Scan(
		&memberID,
		&memberWorkspaceID,
		&memberType,
		&memberStatus,
	); err != nil {
		return ownershipRevalidationError("lock expected Agent member", err)
	}
	if memberID != expected.MemberID ||
		memberWorkspaceID != expected.WorkspaceID ||
		memberType != "agent" ||
		memberStatus != "active" {
		return storeerr.ErrForbidden
	}
	return nil
}

func ownershipRevalidationError(operation string, err error) error {
	if coredb.IsNotFound(err) {
		return storeerr.ErrForbidden
	}
	return fmt.Errorf("%s: %w", operation, err)
}

// MachineCreatedByUser resolves the user that created a machine.
func MachineCreatedByUser(ctx context.Context, queryer coredb.QueryRower, workspaceID string, machineID string) (string, error) {
	row := queryer.QueryRow(ctx, `
SELECT created_by_user_id
FROM machines
WHERE workspace_id = $1 AND id = $2
  AND status = 'active'
`, workspaceID, machineID)

	var createdByUserID string
	if err := row.Scan(&createdByUserID); err != nil {
		if coredb.IsNotFound(err) {
			return "", storeerr.ErrNotFound
		}
		return "", fmt.Errorf("load machine creator: %w", err)
	}
	return createdByUserID, nil
}

// AgentManagedByUser resolves the user that owns the machine backing an agent.
func AgentManagedByUser(ctx context.Context, queryer coredb.QueryRower, workspaceID string, agentID string) (string, error) {
	row := queryer.QueryRow(ctx, `
SELECT machines.created_by_user_id
FROM agents
JOIN machines
  ON machines.workspace_id = agents.workspace_id
 AND machines.id = agents.machine_id
WHERE agents.workspace_id = $1 AND agents.id = $2
  AND agents.status <> 'archived'
  AND machines.status = 'active'
`, workspaceID, agentID)

	var createdByUserID string
	if err := row.Scan(&createdByUserID); err != nil {
		if coredb.IsNotFound(err) {
			return "", storeerr.ErrNotFound
		}
		return "", fmt.Errorf("load agent manager: %w", err)
	}
	return createdByUserID, nil
}
